Skip to content

Commit

Permalink
Merge pull request #47 from migueldeoleiros/feature/window-controls
Browse files Browse the repository at this point in the history
Add new commands for window controls
  • Loading branch information
migueldeoleiros authored Oct 31, 2024
2 parents 8a4b3d4 + 972dc93 commit 24b4ec4
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 19 deletions.
3 changes: 2 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ deps = [
dependency('wayland-server'),
dependency('xkbcommon'),
dependency('libconfig'),
dependency('json-c')
dependency('json-c'),
dependency('uuid')
]

executable(
Expand Down
145 changes: 137 additions & 8 deletions src/commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
#include "src/server.h"
#include "src/toplevel.h"
#include "src/workspace.h"
#include "wlr/util/log.h"
#include <stdio.h>
#include <string.h>
#include <wayland-util.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <json-c/json.h>

Expand All @@ -37,13 +39,19 @@ void window_command(char *tokens[], int ntokens, char *response,
void window_list_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context);
void window_switch_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context);
void window_cycle_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context);
void window_kill_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context);
void window_move_to_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context);
void workspace_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context);
void workspace_list_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context);
struct turtile_context *context);
void workspace_switch_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context);
struct turtile_context *conntext);
typedef struct {
char *cmd_name;
char *subcmd_name;
Expand All @@ -56,6 +64,9 @@ static command_t commands[] = {
{"exit", NULL, exit_command},
{"window", "list", window_list_command},
{"window", "switch", window_switch_command},
{"window", "cycle", window_cycle_command},
{"window", "kill", window_kill_command},
{"window", "move-to", window_move_to_command},
{"window", NULL, window_command},
{"workspace", "list", workspace_list_command},
{"workspace", "switch", workspace_switch_command},
Expand Down Expand Up @@ -148,9 +159,15 @@ void window_list_command(char *tokens[], int ntokens, char *response,
if (toplevel->xdg_toplevel) {
const char *title = toplevel->xdg_toplevel->title ?
toplevel->xdg_toplevel->title : "Unnamed";
const char *app = toplevel->xdg_toplevel->app_id ?
toplevel->xdg_toplevel->title : "null";

// Create a JSON object for each window and populate its fields
struct json_object *json_window = json_object_new_object();
json_object_object_add(json_window, "id",
json_object_new_string(toplevel->id));
json_object_object_add(json_window, "app",
json_object_new_string(app));
json_object_object_add(json_window, "title",
json_object_new_string(title));
json_object_object_add(json_window, "workspace",
Expand All @@ -166,21 +183,133 @@ void window_list_command(char *tokens[], int ntokens, char *response,

void window_switch_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context){
// Cycle to the next toplevel
// TODO: add option to switch window by name
// Switch focus to designated toplevel
struct turtile_server *server = context->server;

if (wl_list_length(&server->toplevels) < 2) {
response = strdup("{\"error\": \"Only one current window open\"}");
return;
if(ntokens >= 1){
char *new_toplevel_id = tokens[0];
struct turtile_toplevel *toplevel;

wl_list_for_each(toplevel, &server->focus_toplevels, flink) {
if(strcmp(toplevel->id, new_toplevel_id) == 0){
focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface);
snprintf(response, MAX_MSG_SIZE,
"{\"success\": \"switching focus to: %s\"}",
toplevel->xdg_toplevel->title);
return;
}
}
snprintf(response, MAX_MSG_SIZE,
"{\"error\": \"window %s not found\"}", new_toplevel_id);

} else{
snprintf(response, MAX_MSG_SIZE,
"{\"error\": \"missing argument: window id\"}");
}
}

void window_cycle_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context){
// Cycle to the next toplevel in the same workspace
struct turtile_server *server = context->server;

struct wl_list workspace_toplevels;
get_workspace_toplevels(server->active_workspace, &workspace_toplevels);

if (wl_list_empty(&workspace_toplevels)){
snprintf(response, MAX_MSG_SIZE,
"{\"error\": \"Workspace is empty\"}");
return;
} else if (wl_list_length(&workspace_toplevels) < 2) {
snprintf(response, MAX_MSG_SIZE,
"{\"error\": \"Only one current window open\"}");
return;
}

struct turtile_toplevel *next_toplevel =
wl_container_of(server->toplevels.prev, next_toplevel, link);
wl_container_of(workspace_toplevels.next, next_toplevel, auxlink);
focus_toplevel(next_toplevel, next_toplevel->xdg_toplevel->base->surface);
snprintf(response, MAX_MSG_SIZE, "{\"success\": \"switching focus to: %s\"}",
next_toplevel->xdg_toplevel->title);
}

void window_kill_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context){
// kill designated toplevel
struct turtile_server *server = context->server;
struct turtile_toplevel *toplevel;

if(ntokens >= 1){
char *new_toplevel_id = tokens[0];

wl_list_for_each(toplevel, &server->focus_toplevels, flink) {
if(strcmp(toplevel->id, new_toplevel_id) == 0){
kill_toplevel(toplevel);
snprintf(response, MAX_MSG_SIZE,
"{\"success\": \"kill: %s\"}",
toplevel->xdg_toplevel->title);
return;
}
}
snprintf(response, MAX_MSG_SIZE,
"{\"error\": \"window %s not found\"}", new_toplevel_id);

} else{
toplevel = get_first_toplevel(server);
kill_toplevel(toplevel);
snprintf(response, MAX_MSG_SIZE,
"{\"success\": \"kill: %s\"}", toplevel->xdg_toplevel->title);
}
}

void window_move_to_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context){
struct turtile_server *server = context->server;

if (ntokens >= 1) {
char *target_workspace_name = tokens[0];
struct turtile_workspace *target_workspace =
get_workspace(server, target_workspace_name);

if (!target_workspace) {
snprintf(response, MAX_MSG_SIZE,
"{\"error\": \"workspace not found\"}");
return;
}

struct turtile_toplevel *toplevel_to_move;
if (ntokens >= 2) {
char *toplevel_id = tokens[1];

toplevel_to_move = get_toplevel(server, toplevel_id);

if (!toplevel_to_move) {
snprintf(response, MAX_MSG_SIZE,
"{\"error\": \"window %s not found\"}", toplevel_id);
return;
}
} else {
toplevel_to_move = get_first_toplevel(server);
if (!toplevel_to_move) {
snprintf(response, MAX_MSG_SIZE,
"{\"error\": \"no focused window to move\"}");
return;
}
}

toplevel_to_move->workspace = target_workspace;
server_redraw_windows(server);

snprintf(response, MAX_MSG_SIZE,
"{\"success\": \"moved window %s to workspace %s\"}",
toplevel_to_move->xdg_toplevel->title, target_workspace->name);

} else {
snprintf(response, MAX_MSG_SIZE,
"{\"error\": \"missing argument: workspace name\"}");
}
}

void workspace_command(char *tokens[], int ntokens, char *response,
struct turtile_context *context){
// TODO: use this function as a help for the other workspace subcommands
Expand Down
39 changes: 39 additions & 0 deletions src/toplevel.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <uuid/uuid.h>

void focus_toplevel(struct turtile_toplevel *toplevel, struct wlr_surface *surface) {
/* Note: this function only deals with keyboard focus. */
Expand Down Expand Up @@ -75,6 +76,37 @@ void focus_toplevel(struct turtile_toplevel *toplevel, struct wlr_surface *surfa
server_redraw_windows(server);
}

void kill_toplevel(struct turtile_toplevel *toplevel) {
struct turtile_server *server = toplevel->server;

struct wl_list workspace_toplevels;
get_workspace_toplevels(server->active_workspace, &workspace_toplevels);

if (wl_list_empty(&workspace_toplevels)){
wlr_xdg_toplevel_send_close(toplevel->xdg_toplevel);
return;
} else if (wl_list_length(&workspace_toplevels) < 2) {
wlr_xdg_toplevel_send_close(toplevel->xdg_toplevel);
return;
}

struct turtile_toplevel *next_toplevel =
wl_container_of(workspace_toplevels.next, next_toplevel, auxlink);
focus_toplevel(next_toplevel, next_toplevel->xdg_toplevel->base->surface);

wlr_xdg_toplevel_send_close(toplevel->xdg_toplevel);
}

struct turtile_toplevel *get_toplevel(struct turtile_server *server, char *id) {
struct turtile_toplevel *toplevel;
wl_list_for_each(toplevel, &server->toplevels, link) {
if (strcmp(toplevel->id, id) == 0) {
return toplevel;
}
}
return NULL;
}

struct turtile_toplevel *get_first_toplevel(struct turtile_server *server) {
struct turtile_toplevel *toplevel;
wl_list_for_each(toplevel, &server->focus_toplevels, flink)
Expand Down Expand Up @@ -131,6 +163,13 @@ void xdg_toplevel_map(struct wl_listener *listener, void *data) {
/* Called when the surface is mapped, or ready to display on-screen. */
struct turtile_toplevel *toplevel = wl_container_of(listener, toplevel, map);

uuid_t uuid;
uuid_generate(uuid);
// Convert first 4 bytes of UUID to a short 8-character hexadecimal string
char short_uuid_str[9]; // 8 characters + null terminator
snprintf(short_uuid_str, sizeof(short_uuid_str), "%08x", *(uint32_t*)uuid);
strncpy(toplevel->id, short_uuid_str, sizeof(toplevel->id));

toplevel->workspace = toplevel->server->active_workspace;
wl_list_insert(&toplevel->server->toplevels, &toplevel->link);
wl_list_insert(&toplevel->server->focus_toplevels, &toplevel->flink);
Expand Down
24 changes: 22 additions & 2 deletions src/toplevel.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,20 @@
#include "src/output.h"
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <uuid/uuid.h>

struct turtile_toplevel {
struct wl_list link;
struct wl_list flink;
struct wl_list auxlink;

char id[9]; // 8 characters + null terminator
struct turtile_server *server;
struct wlr_xdg_toplevel *xdg_toplevel;
struct wlr_scene_tree *scene_tree;
struct turtile_workspace *workspace;
struct wlr_box geometry;

struct wl_listener map;
struct wl_listener unmap;
struct wl_listener commit;
Expand All @@ -43,8 +49,6 @@ struct turtile_toplevel {
struct wl_listener request_resize;
struct wl_listener request_maximize;
struct wl_listener request_fullscreen;

struct turtile_workspace *workspace;
};

/**
Expand All @@ -56,6 +60,22 @@ struct turtile_toplevel {
*/
void focus_toplevel(struct turtile_toplevel *toplevel,
struct wlr_surface *surface);
/**
* Send close request to toplevel, and focus next window
*
* @param toplevel The turtile toplevel to kill.
*/
void kill_toplevel(struct turtile_toplevel *toplevel);

/**
* Retrieves the toplevel with the given ID from the given server.
*
* @param server The turtile server to search for the toplevel on.
* @param id The ID of the toplevel to retrieve.
* @return A pointer to the toplevel with the given ID, or NULL
*/
struct turtile_toplevel *get_toplevel(struct turtile_server *server, char *id);

/**
* Retrieves the first toplevel on the active workspace of the given server.
* If no such toplevel is found, NULL is returned.
Expand Down
22 changes: 22 additions & 0 deletions src/workspace.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ struct turtile_workspace* create_workspace(struct turtile_server *server,
return new_workspace;
}

struct turtile_workspace *get_workspace(struct turtile_server *server,
char *name) {
struct turtile_workspace *workspace;
wl_list_for_each(workspace, &server->workspaces, link) {
if (strcmp(workspace->name, name) == 0) {
return workspace;
}
}
return NULL;
}

void switch_workspace(struct turtile_workspace *workspace){
if(workspace == NULL){
return;
Expand All @@ -65,3 +76,14 @@ struct turtile_workspace* create_workspaces_from_config(struct turtile_server *s
}
return active_workspace;
}

void get_workspace_toplevels(struct turtile_workspace *workspace,
struct wl_list *toplevels) {
struct turtile_server *server = workspace->server;
wl_list_init(toplevels);

struct turtile_toplevel *toplevel;
wl_list_for_each(toplevel, &server->focus_toplevels, flink)
if (toplevel->workspace == workspace)
wl_list_insert(toplevels, &toplevel->auxlink);
}
19 changes: 19 additions & 0 deletions src/workspace.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ struct turtile_workspace {
*/
struct turtile_workspace* create_workspace(struct turtile_server *server,
char *name);

/**
* Retrieves the workspace with the given name from the given server.
*
* @param server The turtile server to search for the workspace on.
* @param name The name of the workspace to retrieve.
* @return A pointer to the workspace with the given name, or NULL
*/
struct turtile_workspace *get_workspace(struct turtile_server *server,
char *name);
/**
* Switches the active workspace to the specified workspace.
*
Expand All @@ -61,4 +71,13 @@ void switch_workspace(struct turtile_workspace *workspace);
*/
struct turtile_workspace* create_workspaces_from_config(struct turtile_server *server);

/**
* Retrieves the list of toplevel windows in the specified workspace.
*
* @param workspace The workspace to retrieve toplevel windows from.
* @param toplevels The list to store the toplevel windows in, making use of
* auxlink
*/
void get_workspace_toplevels(struct turtile_workspace *workspace,
struct wl_list *toplevels);
#endif // WORKSPACE_H
Loading

0 comments on commit 24b4ec4

Please sign in to comment.