chore: update c, d, h files
This commit is contained in:
parent
250e3e2978
commit
f64aedc1fc
BIN
build/ai.o
BIN
build/ai.o
Binary file not shown.
BIN
build/config.o
BIN
build/config.o
Binary file not shown.
BIN
build/demo.o
BIN
build/demo.o
Binary file not shown.
@ -20,7 +20,7 @@ build/main.o: src/main.c include/dwn.h include/config.h include/dwn.h \
|
||||
/usr/include/dbus-1.0/dbus/dbus-syntax.h \
|
||||
/usr/include/dbus-1.0/dbus/dbus-threads.h include/systray.h \
|
||||
include/news.h include/applauncher.h include/ai.h include/autostart.h \
|
||||
include/demo.h include/util.h
|
||||
include/services.h include/api.h include/demo.h include/util.h
|
||||
include/dwn.h:
|
||||
include/config.h:
|
||||
include/dwn.h:
|
||||
@ -55,5 +55,7 @@ include/news.h:
|
||||
include/applauncher.h:
|
||||
include/ai.h:
|
||||
include/autostart.h:
|
||||
include/services.h:
|
||||
include/api.h:
|
||||
include/demo.h:
|
||||
include/util.h:
|
||||
|
||||
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
16
include/api.h
Normal file
16
include/api.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* DWN - Desktop Window Manager
|
||||
* WebSocket API
|
||||
*/
|
||||
|
||||
#ifndef DWN_API_H
|
||||
#define DWN_API_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void api_init(void);
|
||||
void api_process(void);
|
||||
void api_cleanup(void);
|
||||
void api_handle_json_command(const char *json_str);
|
||||
|
||||
#endif
|
||||
@ -60,6 +60,10 @@ struct Config {
|
||||
bool autostart_xdg;
|
||||
char autostart_path[512];
|
||||
|
||||
char services_path[512];
|
||||
bool api_enabled;
|
||||
int api_port;
|
||||
|
||||
int demo_step_delay_ms;
|
||||
int demo_ai_timeout_ms;
|
||||
int demo_window_timeout_ms;
|
||||
|
||||
3013
include/mongoose.h
Normal file
3013
include/mongoose.h
Normal file
File diff suppressed because it is too large
Load Diff
13
include/services.h
Normal file
13
include/services.h
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* DWN - Desktop Window Manager
|
||||
* Service management
|
||||
*/
|
||||
|
||||
#ifndef DWN_SERVICES_H
|
||||
#define DWN_SERVICES_H
|
||||
|
||||
void services_init(void);
|
||||
void services_run(void);
|
||||
void services_cleanup(void);
|
||||
|
||||
#endif
|
||||
39
src/ai.c
39
src/ai.c
@ -390,6 +390,32 @@ static void ai_command_response_callback(AIRequest *req)
|
||||
}
|
||||
|
||||
if (req->state == AI_STATE_COMPLETED && req->response != NULL) {
|
||||
char *wm_cmd = strstr(req->response, "[WM_CMD:");
|
||||
if (wm_cmd != NULL) {
|
||||
char *cmd_start = strchr(wm_cmd, ':');
|
||||
if (cmd_start != NULL) {
|
||||
cmd_start++;
|
||||
while (*cmd_start == ' ') cmd_start++;
|
||||
|
||||
char *cmd_end = strchr(cmd_start, ']');
|
||||
if (cmd_end != NULL) {
|
||||
size_t cmd_len = cmd_end - cmd_start;
|
||||
char *cmd_json = dwn_malloc(cmd_len + 1);
|
||||
strncpy(cmd_json, cmd_start, cmd_len);
|
||||
cmd_json[cmd_len] = '\0';
|
||||
|
||||
LOG_INFO("AI executing WM command: %s", cmd_json);
|
||||
notification_show("DWN AI", "Executing WM Command", cmd_json, NULL, 2000);
|
||||
|
||||
/* Forward to our new API handler logic */
|
||||
extern void api_handle_json_command(const char *json_str);
|
||||
api_handle_json_command(cmd_json);
|
||||
|
||||
dwn_free(cmd_json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *run_cmd = strstr(req->response, "[RUN:");
|
||||
if (run_cmd == NULL) {
|
||||
run_cmd = strstr(req->response, "[EXEC:");
|
||||
@ -471,13 +497,16 @@ void ai_show_command_palette(void)
|
||||
"You can execute shell commands for the user.\n\n"
|
||||
"IMPORTANT: When the user asks you to run, open, launch, or start an application, "
|
||||
"respond with the command in this exact format: [RUN: command]\n"
|
||||
"When the user asks for window management tasks (switching workspaces, focusing windows), "
|
||||
"respond with the JSON command in this exact format: [WM_CMD: {\"command\": \"...\", ...}]\n"
|
||||
"Available WM Commands:\n"
|
||||
"- switch_workspace (e.g. [WM_CMD: {\"command\": \"switch_workspace\", \"workspace\": 2}])\n"
|
||||
"- focus_client (e.g. [WM_CMD: {\"command\": \"focus_client\", \"window\": <window_id>}])\n"
|
||||
"Examples:\n"
|
||||
"- User: 'open chrome' -> [RUN: google-chrome]\n"
|
||||
"- User: 'run firefox' -> [RUN: firefox]\n"
|
||||
"- User: 'open file manager' -> [RUN: thunar]\n"
|
||||
"- User: 'launch terminal' -> [RUN: xfce4-terminal]\n"
|
||||
"- User: 'open vs code' -> [RUN: code]\n\n"
|
||||
"For questions or non-command requests, respond briefly (1-2 sentences) without the [RUN:] format.\n\n"
|
||||
"- User: 'switch to workspace 3' -> [WM_CMD: {\"command\": \"switch_workspace\", \"workspace\": 2}]\n"
|
||||
"- User: 'go to desktop 1' -> [WM_CMD: {\"command\": \"switch_workspace\", \"workspace\": 0}]\n"
|
||||
"For questions or non-command requests, respond briefly (1-2 sentences) without the [RUN:] or [WM_CMD:] format.\n\n"
|
||||
"User's current task: %s\n"
|
||||
"User's request: %s",
|
||||
task, input);
|
||||
|
||||
171
src/api.c
Normal file
171
src/api.c
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* DWN - Desktop Window Manager
|
||||
* WebSocket API implementation using Mongoose and cJSON
|
||||
*/
|
||||
|
||||
#include "api.h"
|
||||
#include "dwn.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
#include "workspace.h"
|
||||
#include "client.h"
|
||||
#include "cJSON.h"
|
||||
#include "mongoose.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct mg_mgr mgr;
|
||||
static bool initialized = false;
|
||||
|
||||
static void handle_get_status(struct mg_connection *c) {
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(root, "status", "ok");
|
||||
cJSON_AddStringToObject(root, "version", DWN_VERSION);
|
||||
cJSON_AddNumberToObject(root, "current_workspace", dwn->current_workspace);
|
||||
cJSON_AddNumberToObject(root, "client_count", dwn->client_count);
|
||||
|
||||
char *json = cJSON_PrintUnformatted(root);
|
||||
if (c) mg_ws_send(c, json, strlen(json), WEBSOCKET_OP_TEXT);
|
||||
free(json);
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
|
||||
static void handle_get_workspaces(struct mg_connection *c) {
|
||||
cJSON *root = cJSON_CreateArray();
|
||||
for (int i = 0; i < MAX_WORKSPACES; i++) {
|
||||
cJSON *ws = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(ws, "id", i);
|
||||
cJSON_AddStringToObject(ws, "name", dwn->workspaces[i].name);
|
||||
cJSON_AddNumberToObject(ws, "layout", (double)dwn->workspaces[i].layout);
|
||||
|
||||
int clients_on_ws = 0;
|
||||
for (Client *cl = dwn->client_list; cl; cl = cl->next) {
|
||||
if (cl->workspace == (unsigned int)i) clients_on_ws++;
|
||||
}
|
||||
cJSON_AddNumberToObject(ws, "client_count", clients_on_ws);
|
||||
cJSON_AddItemToArray(root, ws);
|
||||
}
|
||||
|
||||
char *json = cJSON_PrintUnformatted(root);
|
||||
if (c) mg_ws_send(c, json, strlen(json), WEBSOCKET_OP_TEXT);
|
||||
free(json);
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
|
||||
static void handle_get_clients(struct mg_connection *c) {
|
||||
cJSON *root = cJSON_CreateArray();
|
||||
for (Client *cl = dwn->client_list; cl; cl = cl->next) {
|
||||
cJSON *item = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(item, "window", (double)cl->window);
|
||||
cJSON_AddStringToObject(item, "title", cl->title);
|
||||
cJSON_AddStringToObject(item, "class", cl->class);
|
||||
cJSON_AddNumberToObject(item, "workspace", (double)cl->workspace);
|
||||
cJSON_AddNumberToObject(item, "x", (double)cl->x);
|
||||
cJSON_AddNumberToObject(item, "y", (double)cl->y);
|
||||
cJSON_AddNumberToObject(item, "width", (double)cl->width);
|
||||
cJSON_AddNumberToObject(item, "height", (double)cl->height);
|
||||
cJSON_AddBoolToObject(item, "focused", (workspace_get_current() && workspace_get_current()->focused == cl));
|
||||
cJSON_AddItemToArray(root, item);
|
||||
}
|
||||
|
||||
char *json = cJSON_PrintUnformatted(root);
|
||||
if (c) mg_ws_send(c, json, strlen(json), WEBSOCKET_OP_TEXT);
|
||||
free(json);
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
|
||||
static void handle_command(struct mg_connection *c, cJSON *json) {
|
||||
cJSON *cmd = cJSON_GetObjectItem(json, "command");
|
||||
if (!cJSON_IsString(cmd)) return;
|
||||
|
||||
if (strcmp(cmd->valuestring, "get_status") == 0) {
|
||||
handle_get_status(c);
|
||||
} else if (strcmp(cmd->valuestring, "get_workspaces") == 0) {
|
||||
handle_get_workspaces(c);
|
||||
} else if (strcmp(cmd->valuestring, "get_clients") == 0) {
|
||||
handle_get_clients(c);
|
||||
} else if (strcmp(cmd->valuestring, "switch_workspace") == 0) {
|
||||
cJSON *id = cJSON_GetObjectItem(json, "workspace");
|
||||
if (cJSON_IsNumber(id)) {
|
||||
workspace_switch(id->valueint);
|
||||
handle_get_status(c);
|
||||
}
|
||||
} else if (strcmp(cmd->valuestring, "run_command") == 0) {
|
||||
cJSON *exec = cJSON_GetObjectItem(json, "exec");
|
||||
if (cJSON_IsString(exec)) {
|
||||
spawn_async(exec->valuestring);
|
||||
cJSON *resp = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(resp, "status", "launched");
|
||||
char *resp_json = cJSON_PrintUnformatted(resp);
|
||||
mg_ws_send(c, resp_json, strlen(resp_json), WEBSOCKET_OP_TEXT);
|
||||
free(resp_json);
|
||||
cJSON_Delete(resp);
|
||||
}
|
||||
} else if (strcmp(cmd->valuestring, "focus_client") == 0) {
|
||||
cJSON *win = cJSON_GetObjectItem(json, "window");
|
||||
if (cJSON_IsNumber(win)) {
|
||||
Client *cl = client_find_by_window((Window)win->valuedouble);
|
||||
if (cl) {
|
||||
if (cl->workspace != (unsigned int)dwn->current_workspace) {
|
||||
workspace_switch(cl->workspace);
|
||||
}
|
||||
client_focus(cl);
|
||||
handle_get_status(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_WS_MSG) {
|
||||
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
|
||||
cJSON *json = cJSON_ParseWithLength(wm->data.buf, wm->data.len);
|
||||
if (json) {
|
||||
handle_command(c, json);
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ws"), NULL)) {
|
||||
mg_ws_upgrade(c, hm, NULL);
|
||||
} else {
|
||||
mg_http_reply(c, 200, "", "DWN WebSocket API is running at /ws\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void api_handle_json_command(const char *json_str) {
|
||||
cJSON *json = cJSON_Parse(json_str);
|
||||
if (json) {
|
||||
handle_command(NULL, json);
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
}
|
||||
|
||||
void api_init(void) {
|
||||
if (!dwn->config->api_enabled) return;
|
||||
|
||||
mg_mgr_init(&mgr);
|
||||
char addr[64];
|
||||
snprintf(addr, sizeof(addr), "http://0.0.0.0:%d", dwn->config->api_port);
|
||||
if (mg_http_listen(&mgr, addr, fn, NULL) == NULL) {
|
||||
LOG_ERROR("Failed to start API server on %s", addr);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("API server started on %s", addr);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void api_process(void) {
|
||||
if (!initialized) return;
|
||||
mg_mgr_poll(&mgr, 0); // Non-blocking poll
|
||||
}
|
||||
|
||||
void api_cleanup(void) {
|
||||
if (!initialized) return;
|
||||
mg_mgr_free(&mgr);
|
||||
initialized = false;
|
||||
}
|
||||
22
src/config.c
22
src/config.c
@ -85,6 +85,14 @@ void config_set_defaults(Config *cfg)
|
||||
dwn_free(autostart_path);
|
||||
}
|
||||
|
||||
char *services_path = expand_path("~/.config/dwn/services.d");
|
||||
if (services_path != NULL) {
|
||||
strncpy(cfg->services_path, services_path, sizeof(cfg->services_path) - 1);
|
||||
dwn_free(services_path);
|
||||
}
|
||||
cfg->api_enabled = true;
|
||||
cfg->api_port = 8777;
|
||||
|
||||
cfg->demo_step_delay_ms = 4000;
|
||||
cfg->demo_ai_timeout_ms = 15000;
|
||||
cfg->demo_window_timeout_ms = 5000;
|
||||
@ -180,6 +188,20 @@ static void handle_config_entry(const char *section, const char *key,
|
||||
dwn_free(expanded);
|
||||
}
|
||||
}
|
||||
} else if (strcmp(section, "services") == 0) {
|
||||
if (strcmp(key, "path") == 0) {
|
||||
char *expanded = expand_path(value);
|
||||
if (expanded != NULL) {
|
||||
strncpy(cfg->services_path, expanded, sizeof(cfg->services_path) - 1);
|
||||
dwn_free(expanded);
|
||||
}
|
||||
}
|
||||
} else if (strcmp(section, "api") == 0) {
|
||||
if (strcmp(key, "enabled") == 0) {
|
||||
cfg->api_enabled = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
|
||||
} else if (strcmp(key, "port") == 0) {
|
||||
cfg->api_port = atoi(value);
|
||||
}
|
||||
} else if (strcmp(section, "demo") == 0) {
|
||||
if (strcmp(key, "step_delay") == 0) {
|
||||
int val = atoi(value);
|
||||
|
||||
11
src/main.c
11
src/main.c
@ -19,6 +19,8 @@
|
||||
#include "applauncher.h"
|
||||
#include "ai.h"
|
||||
#include "autostart.h"
|
||||
#include "services.h"
|
||||
#include "api.h"
|
||||
#include "demo.h"
|
||||
#include "util.h"
|
||||
|
||||
@ -777,12 +779,17 @@ int dwn_init(void)
|
||||
autostart_init();
|
||||
autostart_run();
|
||||
|
||||
services_init();
|
||||
services_run();
|
||||
|
||||
keys_init();
|
||||
|
||||
notifications_init();
|
||||
|
||||
ai_init();
|
||||
|
||||
api_init();
|
||||
|
||||
demo_init();
|
||||
|
||||
atoms_setup_ewmh();
|
||||
@ -813,10 +820,12 @@ void dwn_cleanup(void)
|
||||
LOG_INFO("DWN shutting down");
|
||||
|
||||
demo_cleanup();
|
||||
api_cleanup();
|
||||
ai_cleanup();
|
||||
notifications_cleanup();
|
||||
news_cleanup();
|
||||
applauncher_cleanup();
|
||||
services_cleanup();
|
||||
autostart_cleanup();
|
||||
keys_cleanup();
|
||||
xembed_cleanup();
|
||||
@ -872,6 +881,8 @@ void dwn_run(void)
|
||||
|
||||
notifications_process_messages();
|
||||
|
||||
api_process();
|
||||
|
||||
ai_process_pending();
|
||||
|
||||
exa_process_pending();
|
||||
|
||||
17594
src/mongoose.c
Normal file
17594
src/mongoose.c
Normal file
File diff suppressed because it is too large
Load Diff
82
src/services.c
Normal file
82
src/services.c
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* DWN - Desktop Window Manager
|
||||
* Service management implementation
|
||||
*/
|
||||
|
||||
#include "services.h"
|
||||
#include "dwn.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
|
||||
void services_init(void)
|
||||
{
|
||||
if (dwn == NULL || dwn->config == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dwn->config->services_path[0] != '\0') {
|
||||
struct stat st;
|
||||
if (stat(dwn->config->services_path, &st) != 0) {
|
||||
if (mkdir(dwn->config->services_path, 0755) == 0) {
|
||||
LOG_INFO("Created services directory: %s", dwn->config->services_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO("Services initialized");
|
||||
}
|
||||
|
||||
void services_run(void)
|
||||
{
|
||||
if (dwn == NULL || dwn->config == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dwn->config->services_path[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
DIR *dir = opendir(dwn->config->services_path);
|
||||
if (dir == NULL) {
|
||||
LOG_DEBUG("Cannot open services directory: %s", dwn->config->services_path);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Running services from: %s", dwn->config->services_path);
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
char full_path[PATH_MAX];
|
||||
int ret = snprintf(full_path, sizeof(full_path), "%s/%s", dwn->config->services_path, entry->d_name);
|
||||
if (ret < 0 || (size_t)ret >= sizeof(full_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (access(full_path, X_OK) != 0) {
|
||||
LOG_DEBUG("Skipping non-executable service: %s", entry->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_INFO("Starting service: %s", entry->d_name);
|
||||
spawn_async(full_path);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void services_cleanup(void)
|
||||
{
|
||||
LOG_DEBUG("Services cleanup");
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user