Add more advanced multi-output support

* Add commands screen, movetoscreen, movetoprevscreen
  * Add option prio to output command to allow for output priority
    specification
This commit is contained in:
project-repo 2021-12-24 15:52:25 +01:00
commit 851a2f8935
7 changed files with 233 additions and 49 deletions

View file

@ -53,6 +53,7 @@
#include "seat.h"
#include "server.h"
#include "xdg_shell.h"
#include "workspace.h"
#if CG_HAS_XWAYLAND
#include "xwayland.h"
#endif
@ -273,6 +274,7 @@ main(int argc, char *argv[]) {
wl_list_init(&server.input_config);
wl_list_init(&server.output_config);
wl_list_init(&server.output_priorities);
server.modes = malloc(4 * sizeof(char *));
if(!server.modes) {
@ -576,11 +578,15 @@ main(int argc, char *argv[]) {
}
{
struct wl_list tmp_list;
wl_list_init(&tmp_list);
wl_list_insert_list(&tmp_list,&server.outputs);
wl_list_init(&server.outputs);
struct cg_output *output, *output_tmp;
wl_list_for_each_safe(output, output_tmp, &server.outputs, link) {
wl_list_for_each_safe(output, output_tmp, &tmp_list, link) {
output_configure(&server, output);
}
server.curr_output=wl_container_of(server.outputs.next,server.curr_output,link);
}
/* Place the cursor to the top left of the output layout. */

View file

@ -593,23 +593,8 @@ into_process(const char *command) {
}
void
keybinding_cycle_outputs(struct cg_server *server, bool reverse) {
if(reverse) {
server->curr_output = wl_container_of(server->curr_output->link.prev,
server->curr_output, link);
} else {
server->curr_output = wl_container_of(server->curr_output->link.next,
server->curr_output, link);
}
if(&server->curr_output->link == &server->outputs) {
if(reverse) {
server->curr_output = wl_container_of(
server->curr_output->link.prev, server->curr_output, link);
} else {
server->curr_output = wl_container_of(
server->curr_output->link.next, server->curr_output, link);
}
}
set_output(struct cg_server *server, struct cg_output *output) {
server->curr_output = output;
seat_set_focus(
server->seat,
server->curr_output->workspaces[server->curr_output->curr_workspace]
@ -617,6 +602,28 @@ keybinding_cycle_outputs(struct cg_server *server, bool reverse) {
message_printf(server->curr_output, "Current Output");
}
void
keybinding_cycle_outputs(struct cg_server *server, bool reverse) {
struct cg_output *output=NULL;
if(reverse) {
output = wl_container_of(server->curr_output->link.prev,
server->curr_output, link);
} else {
output = wl_container_of(server->curr_output->link.next,
server->curr_output, link);
}
if(&output->link == &server->outputs) {
if(reverse) {
output = wl_container_of(
output->link.prev, output, link);
} else {
output = wl_container_of(
output->link.next, output, link);
}
}
set_output(server,output);
}
/* Cycle through views, whereby the workspace does not change */
void
keybinding_cycle_views(struct cg_server *server, bool reverse) {
@ -718,7 +725,7 @@ keybinding_show_info(struct cg_server *server) {
}
void
keybinding_move_view_to_next_output(struct cg_server *server) {
keybinding_move_view_to_cycle_output(struct cg_server *server, bool reverse) {
if(wl_list_length(&server->outputs) <= 1) {
return;
}
@ -736,7 +743,7 @@ keybinding_move_view_to_next_output(struct cg_server *server) {
seat_set_focus(server->seat, NULL);
}
}
keybinding_cycle_outputs(server, false);
keybinding_cycle_outputs(server, reverse);
if(view != NULL) {
wl_list_insert(&server->curr_output
->workspaces[server->curr_output->curr_workspace]
@ -831,6 +838,49 @@ keybinding_set_background(struct cg_server *server, float *bg) {
server->bg_color[2] = bg[2];
}
void
keybinding_switch_output(struct cg_server *server, int output) {
struct cg_output *it;
int count=1;
wl_list_for_each(it,&server->outputs,link) {
if(count==output) {
set_output(server,it);
return;
}
++count;
}
message_printf(server->curr_output,"Output %d does not exist",output);
return;
}
void
keybinding_move_view_to_output(struct cg_server *server, int output_num) {
struct cg_view *view =
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view;
if(view != NULL) {
wl_list_remove(&view->link);
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view = NULL;
keybinding_cycle_views(server, false);
if(server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view == NULL) {
seat_set_focus(server->seat, NULL);
}
}
keybinding_switch_output(server, output_num);
if(view != NULL) {
struct cg_workspace *ws =
server->curr_output
->workspaces[server->curr_output->curr_workspace];
view->workspace = ws;
wl_list_insert(&ws->views, &view->link);
ws->focused_tile->view = view;
view_maximize(view, ws->focused_tile);
seat_set_focus(server->seat, view);
}
}
void
keybinding_move_view_to_workspace(struct cg_server *server, uint32_t ws) {
struct cg_view *view =
@ -859,6 +909,22 @@ keybinding_move_view_to_workspace(struct cg_server *server, uint32_t ws) {
}
}
void
merge_config(struct cg_output_config *config_new, struct cg_output_config *config_old) {
if(config_new->status==OUTPUT_DEFAULT) {
config_new->status=config_old->status;
}
if(config_new->pos.x == -1) {
config_new->pos=config_old->pos;
}
if(config_new->refresh_rate==0) {
config_new->refresh_rate=config_old->refresh_rate;
}
if(config_new->priority==-1) {
config_new->priority=config_old->priority;
}
}
void
keybinding_configure_output(struct cg_server *server,
struct cg_output_config *cfg) {
@ -877,6 +943,7 @@ keybinding_configure_output(struct cg_server *server,
wl_list_for_each_safe(it, tmp, &server->output_config, link) {
if(strcmp(config->output_name, it->output_name) == 0) {
wl_list_remove(&it->link);
merge_config(config, it);
free(it->output_name);
free(it);
}
@ -944,6 +1011,9 @@ run_action(enum keybinding_action action, struct cg_server *server,
case KEYBINDING_SWITCH_WORKSPACE:
keybinding_switch_ws(server, data.u);
break;
case KEYBINDING_SWITCH_OUTPUT:
keybinding_switch_output(server, data.u);
break;
case KEYBINDING_SWITCH_MODE:
server->seat->mode = data.u;
break;
@ -969,6 +1039,10 @@ run_action(enum keybinding_action action, struct cg_server *server,
keybinding_move_view_to_workspace(server, data.u);
break;
}
case KEYBINDING_MOVE_VIEW_TO_OUTPUT: {
keybinding_move_view_to_output(server, data.u);
break;
}
case KEYBINDING_SWAP_LEFT: {
swap_tile_left(
server->curr_output->workspaces[server->curr_output->curr_workspace]
@ -1017,8 +1091,8 @@ run_action(enum keybinding_action action, struct cg_server *server,
->focused_tile);
break;
}
case KEYBINDING_MOVE_VIEW_TO_NEXT_OUTPUT: {
keybinding_move_view_to_next_output(server);
case KEYBINDING_MOVE_VIEW_TO_CYCLE_OUTPUT: {
keybinding_move_view_to_cycle_output(server,data.b);
break;
}
case KEYBINDING_DEFINEKEY:

View file

@ -29,6 +29,7 @@ enum keybinding_action {
// configuration
KEYBINDING_QUIT,
KEYBINDING_NOOP,
KEYBINDING_SWITCH_OUTPUT, // data.u is the desired output
KEYBINDING_SWITCH_WORKSPACE, // data.u is the desired workspace
KEYBINDING_SWITCH_MODE, // data.u is the desired mode
KEYBINDING_SWITCH_DEFAULT_MODE, // data.u is the desired mode
@ -37,7 +38,8 @@ enum keybinding_action {
KEYBINDING_RESIZE_TILE_VERTICAL, // data.i is the number of pixels to add to
// the current height
KEYBINDING_MOVE_VIEW_TO_WORKSPACE, // data.u is the desired workspace
KEYBINDING_MOVE_VIEW_TO_NEXT_OUTPUT,
KEYBINDING_MOVE_VIEW_TO_OUTPUT, // data.u is the desired output
KEYBINDING_MOVE_VIEW_TO_CYCLE_OUTPUT, //data.b is 0 if forward, 1 if reverse
KEYBINDING_SHOW_TIME,
KEYBINDING_SHOW_INFO,

View file

@ -564,6 +564,29 @@ output_set_mode(struct wlr_output *output, int width, int height,
wlr_output_set_mode(output, best);
}
void
output_insert(struct cg_server *server, struct cg_output *output) {
struct cg_output *it, *prev_it=NULL;
bool first=true;
wl_list_for_each(it,&server->outputs,link) {
if(it->priority<output->priority) {
if(first == true) {
wl_list_insert(&server->outputs,&output->link);
} else {
wl_list_insert(it->link.prev,&output->link);
}
return;
}
first=false;
prev_it=it;
}
if(prev_it==NULL) {
wl_list_insert(&server->outputs,&output->link);
} else {
wl_list_insert(&prev_it->link,&output->link);
}
}
void
output_configure(struct cg_server *server, struct cg_output *output) {
struct wlr_output *wlr_output = output->wlr_output;
@ -577,7 +600,7 @@ output_configure(struct cg_server *server, struct cg_output *output) {
wlr_output_layout_remove(server->output_layout, wlr_output);
}
if(config == NULL || config->status == OUTPUT_ENABLE) {
if(config == NULL) {
wlr_output_layout_add_auto(server->output_layout, wlr_output);
struct wlr_output_mode *preferred_mode =
@ -586,23 +609,30 @@ output_configure(struct cg_server *server, struct cg_output *output) {
wlr_output_set_mode(wlr_output, preferred_mode);
}
wl_list_remove(&output->link);
wl_list_insert(&server->outputs, &output->link);
output_insert(server,output);
wlr_output_enable(wlr_output, true);
wlr_output_commit(wlr_output);
} else if(config->status == OUTPUT_DISABLE) {
output_clear(output);
wl_list_insert(&server->disabled_outputs, &output->link);
wlr_output_enable(wlr_output, false);
wlr_output_commit(wlr_output);
} else {
wl_list_remove(&output->link);
wl_list_insert(&server->outputs, &output->link);
output_set_mode(wlr_output, config->pos.width, config->pos.height,
config->refresh_rate);
wlr_output_layout_add(server->output_layout, wlr_output, config->pos.x,
config->pos.y);
wlr_output_enable(wlr_output, true);
wlr_output_commit(wlr_output);
if(config->status == OUTPUT_DISABLE) {
output_clear(output);
wl_list_insert(&server->disabled_outputs, &output->link);
wlr_output_enable(wlr_output, false);
wlr_output_commit(wlr_output);
} else {
wl_list_remove(&output->link);
if(config->priority != -1) {
output->priority=config->priority;
}
if(config->pos.x != -1) {
output_set_mode(wlr_output, config->pos.width, config->pos.height,
config->refresh_rate);
wlr_output_layout_add(server->output_layout, wlr_output, config->pos.x,
config->pos.y);
}
output_insert(server,output);
wlr_output_enable(wlr_output, true);
wlr_output_commit(wlr_output);
}
}
}
@ -626,17 +656,14 @@ handle_new_output(struct wl_listener *listener, void *data) {
output->damage = wlr_output_damage_create(wlr_output);
output->last_scanned_out_view = NULL;
output->mode.notify = handle_output_mode;
wl_signal_add(&wlr_output->events.mode, &output->mode);
output->commit.notify = handle_output_commit;
wl_signal_add(&wlr_output->events.commit, &output->commit);
output->destroy.notify = handle_output_destroy;
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
output->damage_frame.notify = handle_output_damage_frame;
wl_signal_add(&output->damage->events.frame, &output->damage_frame);
output->damage_destroy.notify = handle_output_damage_destroy;
wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
struct cg_output_priorities *it;
int prio=-1;
wl_list_for_each(it, &server->output_priorities, link) {
if(strcmp(output->wlr_output->name,it->ident)==0) {
prio=it->priority;
}
}
output->priority=prio;
wlr_output_set_transform(wlr_output, server->output_transform);
output->workspaces = NULL;
@ -650,7 +677,7 @@ handle_new_output(struct wl_listener *listener, void *data) {
wlr_output->name, wlr_output->scale);
}
wl_list_insert(&server->outputs, &output->link);
output_insert(server,output);
output_configure(server, output);
wlr_output_damage_add_whole(output->damage);
@ -672,6 +699,17 @@ handle_new_output(struct wl_listener *listener, void *data) {
wlr_xcursor_manager_set_cursor_image(server->seat->xcursor_manager,
DEFAULT_XCURSOR, server->seat->cursor);
wlr_cursor_warp(server->seat->cursor, NULL, 0, 0);
output->mode.notify = handle_output_mode;
wl_signal_add(&wlr_output->events.mode, &output->mode);
output->commit.notify = handle_output_commit;
wl_signal_add(&wlr_output->events.commit, &output->commit);
output->destroy.notify = handle_output_destroy;
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
output->damage_frame.notify = handle_output_damage_frame;
wl_signal_add(&output->damage->events.frame, &output->damage_frame);
output->damage_destroy.notify = handle_output_damage_destroy;
wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
}
#if CG_HAS_FANALYZE
#pragma GCC diagnostic pop

View file

@ -23,11 +23,18 @@ struct cg_output {
struct cg_workspace **workspaces;
struct wl_list messages;
int curr_workspace;
int priority;
struct cg_view *last_scanned_out_view;
struct wl_list link; // cg_server::outputs
};
struct cg_output_priorities {
char *ident;
int priority;
struct wl_list link;
};
enum output_status { OUTPUT_ENABLE, OUTPUT_DISABLE, OUTPUT_DEFAULT };
struct cg_output_config {
@ -35,6 +42,7 @@ struct cg_output_config {
struct wlr_box pos;
char *output_name;
float refresh_rate;
int priority;
struct wl_list link; // cg_server::output_config
};

61
parse.c
View file

@ -503,6 +503,8 @@ parse_output_config_keyword(char *key_str, enum output_status *status) {
}
if(strcmp(key_str, "pos") == 0) {
*status = OUTPUT_DEFAULT;
} else if(strcmp(key_str, "prio") == 0) {
*status = OUTPUT_DEFAULT;
} else if(strcmp(key_str, "enable") == 0) {
*status = OUTPUT_ENABLE;
} else if(strcmp(key_str, "disable") == 0) {
@ -521,6 +523,11 @@ parse_output_config(char **saveptr, char **errstr) {
log_error("Failed to allocate memory for output configuration");
goto error;
}
cfg->status=OUTPUT_DEFAULT;
cfg->pos.x=-1;
cfg->output_name=NULL;
cfg->refresh_rate=0;
cfg->priority=-1;
char *name = strtok_r(NULL, " ", saveptr);
if(name == NULL) {
*errstr =
@ -529,7 +536,7 @@ parse_output_config(char **saveptr, char **errstr) {
}
char *key_str = strtok_r(NULL, " ", saveptr);
if(parse_output_config_keyword(key_str, &(cfg->status)) != 0) {
*errstr = log_error("Expected keyword \"pos\", \"enable\" or "
*errstr = log_error("Expected keyword \"pos\", \"prio\", \"enable\" or "
"\"disable\" in output configuration for output %s",
name);
goto error;
@ -539,6 +546,16 @@ parse_output_config(char **saveptr, char **errstr) {
return cfg;
}
if(strcmp(key_str, "prio") == 0) {
cfg->priority=parse_uint(saveptr, " ");
if(cfg->priority<0) {
*errstr = log_error( "Error parsing priority of output configuration for output %s", name);
goto error;
}
cfg->output_name = strdup(name);
return cfg;
}
cfg->pos.x = parse_uint(saveptr, " ");
if(cfg->pos.x < 0) {
*errstr = log_error(
@ -669,6 +686,23 @@ parse_command(struct cg_server *server, struct keybinding *keybinding,
} else if(strcmp(action, "resizeup") == 0) {
keybinding->action = KEYBINDING_RESIZE_TILE_VERTICAL;
keybinding->data.i = -10;
} else if(strcmp(action, "screen") == 0) {
keybinding->action = KEYBINDING_SWITCH_OUTPUT;
char *noutp_str = strtok_r(NULL, " ", &saveptr);
if(noutp_str == NULL) {
*errstr = log_error(
"Expected argument for \"output\" action, got none.");
return -1;
}
long outp = strtol(noutp_str, NULL, 10);
if(outp < 1) {
*errstr = log_error("Workspace number must be an integer number "
"larger or equal to 1. Got %ld",
outp);
return -1;
}
keybinding->data.u = outp;
} else if(strcmp(action, "workspace") == 0) {
keybinding->action = KEYBINDING_SWITCH_WORKSPACE;
char *nws_str = strtok_r(NULL, " ", &saveptr);
@ -686,12 +720,29 @@ parse_command(struct cg_server *server, struct keybinding *keybinding,
return -1;
}
keybinding->data.u = ws - 1;
} else if(strcmp(action, "movetoscreen") == 0) {
keybinding->action = KEYBINDING_MOVE_VIEW_TO_OUTPUT;
char *noutp_str = strtok_r(NULL, " ", &saveptr);
if(noutp_str == NULL) {
*errstr = log_error(
"Expected argument for \"movetoscreen\" action, got none.");
return -1;
}
long outp = strtol(noutp_str, NULL, 10);
if(outp < 1) {
*errstr = log_error("Output number must be an integer larger or "
"equal to 1. Got %ld",
outp);
return -1;
}
keybinding->data.u = outp;
} else if(strcmp(action, "movetoworkspace") == 0) {
keybinding->action = KEYBINDING_MOVE_VIEW_TO_WORKSPACE;
char *nws_str = strtok_r(NULL, " ", &saveptr);
if(nws_str == NULL) {
*errstr = log_error(
"Expected argument for \"workspace\" action, got none.");
"Expected argument for \"movetoworkspace\" action, got none.");
return -1;
}
@ -720,7 +771,11 @@ parse_command(struct cg_server *server, struct keybinding *keybinding,
} else if(strcmp(action, "focusdown") == 0) {
keybinding->action = KEYBINDING_FOCUS_BOTTOM;
} else if(strcmp(action, "movetonextscreen") == 0) {
keybinding->action = KEYBINDING_MOVE_VIEW_TO_NEXT_OUTPUT;
keybinding->action = KEYBINDING_MOVE_VIEW_TO_CYCLE_OUTPUT;
keybinding->data.b=false;
} else if(strcmp(action, "movetoprevscreen") == 0) {
keybinding->action = KEYBINDING_MOVE_VIEW_TO_CYCLE_OUTPUT;
keybinding->data.b=true;
} else if(strcmp(action, "switchvt") == 0) {
keybinding->action = KEYBINDING_CHANGE_TTY;
char *ntty = strtok_r(NULL, " ", &saveptr);

View file

@ -32,6 +32,7 @@ struct cg_server {
struct wl_list outputs;
struct cg_output *curr_output;
struct wl_listener new_output;
struct wl_list output_priorities;
struct wl_listener xdg_toplevel_decoration;
struct wl_listener new_xdg_shell_surface;