Merge branch 'master' into master

This commit is contained in:
yuanye100 2022-07-07 16:20:04 +08:00 committed by GitHub
commit 6003c62c1f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 446 additions and 249 deletions

View file

@ -2,19 +2,23 @@
#ifndef __LABWC_ACTION_H #ifndef __LABWC_ACTION_H
#define __LABWC_ACTION_H #define __LABWC_ACTION_H
struct server;
struct view; struct view;
struct server;
struct wl_list;
struct action { struct action {
uint32_t type; struct wl_list link; /* struct keybinding.actions,
char *arg; * struct mousebinding.actions,
struct wl_list link; * struct menuitem.actions */
uint32_t type; /* enum action_type */
struct wl_list args; /* struct action_arg.link */
}; };
struct action *action_create(const char *action_name); struct action *action_create(const char *action_name);
void action_list_free(struct wl_list *action_list); void action_arg_add_str(struct action *action, char *key, const char *value);
void actions_run(struct view *activator, struct server *server, void actions_run(struct view *activator, struct server *server,
struct wl_list *actions, uint32_t resize_edges); struct wl_list *actions, uint32_t resize_edges);
void action_list_free(struct wl_list *action_list);
#endif #endif /* __LABWC_ACTION_H */

View file

@ -11,8 +11,8 @@ struct keybind {
uint32_t modifiers; uint32_t modifiers;
xkb_keysym_t *keysyms; xkb_keysym_t *keysyms;
size_t keysyms_len; size_t keysyms_len;
struct wl_list actions; struct wl_list actions; /* struct action.link */
struct wl_list link; struct wl_list link; /* struct rcxml.keybinds */
}; };
/** /**

View file

@ -26,9 +26,9 @@ struct mousebind {
/* ex: doubleclick, press, drag */ /* ex: doubleclick, press, drag */
enum mouse_event mouse_event; enum mouse_event mouse_event;
struct wl_list actions; struct wl_list actions; /* struct action.link */
struct wl_list link; /* rcxml::mousebinds */ struct wl_list link; /* struct rcxml.mousebinds */
bool pressed_in_context; /* used in click events */ bool pressed_in_context; /* used in click events */
}; };

View file

@ -37,11 +37,11 @@ struct rcxml {
/* keyboard */ /* keyboard */
int repeat_rate; int repeat_rate;
int repeat_delay; int repeat_delay;
struct wl_list keybinds; struct wl_list keybinds; /* struct keybind.link */
/* mouse */ /* mouse */
long doubleclick_time; /* in ms */ long doubleclick_time; /* in ms */
struct wl_list mousebinds; struct wl_list mousebinds; /* struct mousebind.link */
/* libinput */ /* libinput */
struct wl_list libinput_categories; struct wl_list libinput_categories;

View file

@ -306,13 +306,14 @@ struct view {
bool been_mapped; bool been_mapped;
bool minimized; bool minimized;
bool maximized; bool maximized;
uint32_t tiled; /* private, enum view_edge in src/view.c */
struct wlr_output *fullscreen; struct wlr_output *fullscreen;
/* geometry of the wlr_surface contained within the view */ /* geometry of the wlr_surface contained within the view */
int x, y, w, h; int x, y, w, h;
/* geometry before maximize */ /* user defined geometry before maximize / tiling / fullscreen */
struct wlr_box unmaximized_geometry; struct wlr_box natural_geometry;
/* /*
* margin refers to the space between the extremities of the * margin refers to the space between the extremities of the
@ -364,6 +365,7 @@ struct view {
struct xwayland_unmanaged { struct xwayland_unmanaged {
struct server *server; struct server *server;
struct wlr_xwayland_surface *xwayland_surface; struct wlr_xwayland_surface *xwayland_surface;
struct wlr_scene_node *node;
struct wl_list link; struct wl_list link;
int lx, ly; int lx, ly;
@ -460,6 +462,7 @@ void foreign_toplevel_handle_create(struct view *view);
void desktop_move_to_front(struct view *view); void desktop_move_to_front(struct view *view);
void desktop_move_to_back(struct view *view); void desktop_move_to_back(struct view *view);
void desktop_focus_and_activate_view(struct seat *seat, struct view *view); void desktop_focus_and_activate_view(struct seat *seat, struct view *view);
void desktop_arrange_all_views(struct server *server);
enum lab_cycle_dir { enum lab_cycle_dir {
LAB_CYCLE_DIR_NONE, LAB_CYCLE_DIR_NONE,

24
include/private/action.h Normal file
View file

@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __LABWC_PRIVATE_ACTION_H
#define __LABWC_PRIVATE_ACTION_H
/* Don't include ourself as search path starts at current directory */
#include "../action.h"
enum action_arg_type {
LAB_ACTION_ARG_STR = 0,
};
struct action_arg {
struct wl_list link; /* struct action.args */
const char *key; /* May be NULL if there is just one arg */
enum action_arg_type type;
};
struct action_arg_str {
struct action_arg base;
char *value;
};
#endif /* __LABWC_PRIVATE_ACTION_H */

View file

@ -134,8 +134,10 @@ struct ssd_hover_state {
/* Public SSD API */ /* Public SSD API */
void ssd_create(struct view *view); void ssd_create(struct view *view);
void ssd_hide(struct view *view); void ssd_hide(struct view *view);
void ssd_set_active(struct view *view, bool activated); void ssd_set_active(struct view *view, bool activated);
void ssd_update_title(struct view *view); void ssd_update_title(struct view *view);
void ssd_update_geometry(struct view *view); void ssd_update_geometry(struct view *view);
void ssd_reload(struct view *view); void ssd_reload(struct view *view);

View file

@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <signal.h> #include <signal.h>
#include <string.h>
#include <strings.h> #include <strings.h>
#include <unistd.h> #include <unistd.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
@ -9,8 +11,8 @@
#include "debug.h" #include "debug.h"
#include "labwc.h" #include "labwc.h"
#include "menu/menu.h" #include "menu/menu.h"
#include "private/action.h"
#include "ssd.h" #include "ssd.h"
#include "action.h"
#include "workspaces.h" #include "workspaces.h"
enum action_type { enum action_type {
@ -64,6 +66,24 @@ const char *action_names[] = {
NULL NULL
}; };
static char *
action_str_from_arg(struct action_arg *arg)
{
assert(arg->type == LAB_ACTION_ARG_STR);
return ((struct action_arg_str *)arg)->value;
}
static struct action_arg *
action_get_first_arg(struct action *action)
{
struct action_arg *arg;
struct wl_list *item = action->args.next;
if (item == &action->args) {
return NULL;
}
return wl_container_of(item, arg, link);
}
static enum action_type static enum action_type
action_type_from_str(const char *action_name) action_type_from_str(const char *action_name)
{ {
@ -85,15 +105,26 @@ action_create(const char *action_name)
} }
struct action *action = calloc(1, sizeof(struct action)); struct action *action = calloc(1, sizeof(struct action));
action->type = action_type_from_str(action_name); action->type = action_type_from_str(action_name);
wl_list_init(&action->args);
return action; return action;
} }
void action_list_free(struct wl_list *action_list) void action_list_free(struct wl_list *action_list)
{ {
struct action_arg *arg, *arg_tmp;
struct action *action, *action_tmp; struct action *action, *action_tmp;
/* Free actions */
wl_list_for_each_safe(action, action_tmp, action_list, link) { wl_list_for_each_safe(action, action_tmp, action_list, link) {
wl_list_remove(&action->link); wl_list_remove(&action->link);
zfree(action->arg); /* Free args */
wl_list_for_each_safe(arg, arg_tmp, &action->args, link) {
wl_list_remove(&arg->link);
zfree(arg->key);
if (arg->type == LAB_ACTION_ARG_STR) {
free(action_str_from_arg(arg));
}
zfree(arg);
}
zfree(action); zfree(action);
} }
} }
@ -151,9 +182,13 @@ actions_run(struct view *activator, struct server *server,
struct view *view; struct view *view;
struct action *action; struct action *action;
struct action_arg *arg;
wl_list_for_each(action, actions, link) { wl_list_for_each(action, actions, link) {
wlr_log(WLR_DEBUG, "Handling action %s (%u) with arg %s", wlr_log(WLR_DEBUG, "Handling action %s (%u)",
action_names[action->type], action->type, action->arg); action_names[action->type], action->type);
/* Get arg now so we don't have to repeat every time we only need one */
arg = action_get_first_arg(action);
/* /*
* Refetch view because it may have been changed due to the * Refetch view because it may have been changed due to the
@ -171,28 +206,30 @@ actions_run(struct view *activator, struct server *server,
debug_dump_scene(server); debug_dump_scene(server);
break; break;
case ACTION_TYPE_EXECUTE: case ACTION_TYPE_EXECUTE:
{ if (!arg) {
struct buf cmd; wlr_log(WLR_ERROR, "Missing argument for Execute");
buf_init(&cmd); break;
buf_add(&cmd, action->arg);
buf_expand_shell_variables(&cmd);
spawn_async_no_shell(cmd.buf);
free(cmd.buf);
} }
struct buf cmd;
buf_init(&cmd);
buf_add(&cmd, action_str_from_arg(arg));
buf_expand_shell_variables(&cmd);
spawn_async_no_shell(cmd.buf);
free(cmd.buf);
break; break;
case ACTION_TYPE_EXIT: case ACTION_TYPE_EXIT:
wl_display_terminate(server->wl_display); wl_display_terminate(server->wl_display);
break; break;
case ACTION_TYPE_MOVE_TO_EDGE: case ACTION_TYPE_MOVE_TO_EDGE:
if (action->arg) { if (arg) {
view_move_to_edge(view, action->arg); view_move_to_edge(view, action_str_from_arg(arg));
} else { } else {
wlr_log(WLR_ERROR, "Missing argument for MoveToEdge"); wlr_log(WLR_ERROR, "Missing argument for MoveToEdge");
} }
break; break;
case ACTION_TYPE_SNAP_TO_EDGE: case ACTION_TYPE_SNAP_TO_EDGE:
if (action->arg) { if (arg) {
view_snap_to_edge(view, action->arg); view_snap_to_edge(view, action_str_from_arg(arg));
} else { } else {
wlr_log(WLR_ERROR, "Missing argument for SnapToEdge"); wlr_log(WLR_ERROR, "Missing argument for SnapToEdge");
} }
@ -211,7 +248,11 @@ actions_run(struct view *activator, struct server *server,
kill(getpid(), SIGHUP); kill(getpid(), SIGHUP);
break; break;
case ACTION_TYPE_SHOW_MENU: case ACTION_TYPE_SHOW_MENU:
show_menu(server, view, action->arg); if (arg) {
show_menu(server, view, action_str_from_arg(arg));
} else {
wlr_log(WLR_ERROR, "Missing argument for ShowMenu");
}
break; break;
case ACTION_TYPE_TOGGLE_MAXIMIZE: case ACTION_TYPE_TOGGLE_MAXIMIZE:
if (view) { if (view) {
@ -263,27 +304,33 @@ actions_run(struct view *activator, struct server *server,
} }
break; break;
case ACTION_TYPE_GO_TO_DESKTOP: case ACTION_TYPE_GO_TO_DESKTOP:
{ if (!arg) {
struct workspace *target; wlr_log(WLR_ERROR, "Missing argument for GoToDesktop");
target = workspaces_find(server->workspace_current, action->arg); break;
if (target) { }
workspaces_switch_to(target); struct workspace *target;
} char *target_name = action_str_from_arg(arg);
target = workspaces_find(server->workspace_current, target_name);
if (target) {
workspaces_switch_to(target);
} }
break; break;
case ACTION_TYPE_SEND_TO_DESKTOP: case ACTION_TYPE_SEND_TO_DESKTOP:
if (!arg) {
wlr_log(WLR_ERROR, "Missing argument for SendToDesktop");
break;
}
if (view) { if (view) {
struct workspace *target; struct workspace *target;
target = workspaces_find(view->workspace, action->arg); char *target_name = action_str_from_arg(arg);
target = workspaces_find(view->workspace, target_name);
if (target) { if (target) {
workspaces_send_to(view, target); workspaces_send_to(view, target);
} }
} }
break; break;
case ACTION_TYPE_NONE: case ACTION_TYPE_NONE:
wlr_log(WLR_ERROR, wlr_log(WLR_ERROR, "Not executing unknown action");
"Not executing unknown action with arg %s",
action->arg);
break; break;
default: default:
/* /*
@ -292,9 +339,21 @@ actions_run(struct view *activator, struct server *server,
* adding a new action without installing a handler here. * adding a new action without installing a handler here.
*/ */
wlr_log(WLR_ERROR, wlr_log(WLR_ERROR,
"Not executing invalid action (%u) with arg %s" "Not executing invalid action (%u)"
" This is a BUG. Please report.", " This is a BUG. Please report.", action->type);
action->type, action->arg);
} }
} }
} }
void
action_arg_add_str(struct action *action, char *key, const char *value)
{
assert(value && "Tried to add NULL action string argument");
struct action_arg_str *arg = calloc(1, sizeof(*arg));
arg->base.type = LAB_ACTION_ARG_STR;
if (key) {
arg->base.key = strdup(key);
}
arg->value = strdup(value);
wl_list_insert(action->args.prev, &arg->base.link);
}

View file

@ -71,21 +71,18 @@ fill_keybind(char *nodename, char *content)
} else if (!current_keybind_action) { } else if (!current_keybind_action) {
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. " wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
"nodename: '%s' content: '%s'", nodename, content); "nodename: '%s' content: '%s'", nodename, content);
} else if (current_keybind_action->arg) {
wlr_log(WLR_ERROR, "Action argument already set: %s",
current_keybind_action->arg);
} else if (!strcmp(nodename, "command.action")) { } else if (!strcmp(nodename, "command.action")) {
/* Execute */ /* Execute */
current_keybind_action->arg = strdup(content); action_arg_add_str(current_keybind_action, NULL, content);
} else if (!strcmp(nodename, "direction.action")) { } else if (!strcmp(nodename, "direction.action")) {
/* MoveToEdge, SnapToEdge */ /* MoveToEdge, SnapToEdge */
current_keybind_action->arg = strdup(content); action_arg_add_str(current_keybind_action, NULL, content);
} else if (!strcmp(nodename, "menu.action")) { } else if (!strcmp(nodename, "menu.action")) {
/* ShowMenu */ /* ShowMenu */
current_keybind_action->arg = strdup(content); action_arg_add_str(current_keybind_action, NULL, content);
} else if (!strcmp(nodename, "to.action")) { } else if (!strcmp(nodename, "to.action")) {
/* GoToDesktop, SendToDesktop */ /* GoToDesktop, SendToDesktop */
current_keybind_action->arg = strdup(content); action_arg_add_str(current_keybind_action, NULL, content);
} }
} }
@ -134,15 +131,12 @@ fill_mousebind(char *nodename, char *content)
} else if (!current_mousebind_action) { } else if (!current_mousebind_action) {
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. " wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
"nodename: '%s' content: '%s'", nodename, content); "nodename: '%s' content: '%s'", nodename, content);
} else if (current_mousebind_action->arg) {
wlr_log(WLR_ERROR, "Action argument already set: %s",
current_mousebind_action->arg);
} else if (!strcmp(nodename, "command.action")) { } else if (!strcmp(nodename, "command.action")) {
current_mousebind_action->arg = strdup(content); action_arg_add_str(current_mousebind_action, NULL, content);
} else if (!strcmp(nodename, "direction.action")) { } else if (!strcmp(nodename, "direction.action")) {
current_mousebind_action->arg = strdup(content); action_arg_add_str(current_mousebind_action, NULL, content);
} else if (!strcmp(nodename, "menu.action")) { } else if (!strcmp(nodename, "menu.action")) {
current_mousebind_action->arg = strdup(content); action_arg_add_str(current_mousebind_action, NULL, content);
} }
} }
@ -544,7 +538,7 @@ load_default_key_bindings(void)
wl_list_insert(k->actions.prev, &action->link); wl_list_insert(k->actions.prev, &action->link);
if (key_combos[i].command) { if (key_combos[i].command) {
action->arg = strdup(key_combos[i].command); action_arg_add_str(action, NULL, key_combos[i].command);
} }
} }
} }
@ -604,7 +598,7 @@ load_default_mouse_bindings(void)
wl_list_insert(m->actions.prev, &action->link); wl_list_insert(m->actions.prev, &action->link);
if (mouse_combos[i].command) { if (mouse_combos[i].command) {
action->arg = strdup(mouse_combos[i].command); action_arg_add_str(action, NULL, mouse_combos[i].command);
} }
} }
} }

View file

@ -99,6 +99,16 @@ deactivate_all_views(struct server *server)
} }
} }
void
desktop_arrange_all_views(struct server *server)
{
/* Adjust window positions/sizes */
struct view *view;
wl_list_for_each(view, &server->views, link) {
view_adjust_for_layout_change(view);
}
}
void void
desktop_focus_and_activate_view(struct seat *seat, struct view *view) desktop_focus_and_activate_view(struct seat *seat, struct view *view)
{ {

View file

@ -17,30 +17,51 @@ max_move_scale(double pos_cursor, double pos_current,
void void
interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
{ {
if (view->maximized) { if (mode == LAB_INPUT_STATE_MOVE && view->fullscreen) {
/**
* We don't allow moving fullscreen windows.
*
* If you think there is a good reason to allow it
* feel free to open an issue explaining your use-case.
*/
return;
}
if (mode == LAB_INPUT_STATE_RESIZE
&& (view->fullscreen || view->maximized)) {
/* We don't allow resizing while in maximized or fullscreen state */
return;
}
if (view->maximized || view->tiled) {
if (mode == LAB_INPUT_STATE_MOVE) { if (mode == LAB_INPUT_STATE_MOVE) {
/* Exit maximized or tiled mode */
int new_x = max_move_scale(view->server->seat.cursor->x, int new_x = max_move_scale(view->server->seat.cursor->x,
view->x, view->w, view->unmaximized_geometry.width); view->x, view->w, view->natural_geometry.width);
int new_y = max_move_scale(view->server->seat.cursor->y, int new_y = max_move_scale(view->server->seat.cursor->y,
view->y, view->h, view->unmaximized_geometry.height); view->y, view->h, view->natural_geometry.height);
view->unmaximized_geometry.x = new_x; view->natural_geometry.x = new_x;
view->unmaximized_geometry.y = new_y; view->natural_geometry.y = new_y;
view_maximize(view, false); if (view->maximized) {
/* view_maximize(view, false);
* view_maximize() indirectly calls view->impl->configure }
* which is async but we are using the current values in if (view->tiled) {
* server->grab_box. We pretend the configure already view_move_resize(view, view->natural_geometry);
* happened by setting them manually. }
/**
* view_maximize() / view_move_resize() indirectly calls
* view->impl->configure which is async but we are using
* the current values in server->grab_box. We pretend the
* configure already happened by setting them manually.
*/ */
view->x = new_x; view->x = new_x;
view->y = new_y; view->y = new_y;
view->w = view->unmaximized_geometry.width; view->w = view->natural_geometry.width;
view->h = view->unmaximized_geometry.height; view->h = view->natural_geometry.height;
} else {
return;
} }
} }
/* Moving or resizing always resets tiled state */
view->tiled = 0;
/* /*
* This function sets up an interactive move or resize operation, where * This function sets up an interactive move or resize operation, where
* the compositor stops propagating pointer events to clients and * the compositor stops propagating pointer events to clients and
@ -101,9 +122,9 @@ interactive_end(struct view *view)
* When unmaximizing later on restore * When unmaximizing later on restore
* original position * original position
*/ */
view->unmaximized_geometry.x = view->natural_geometry.x =
view->server->grab_box.x; view->server->grab_box.x;
view->unmaximized_geometry.y = view->natural_geometry.y =
view->server->grab_box.y; view->server->grab_box.y;
} else { } else {
view_snap_to_edge(view, "up"); view_snap_to_edge(view, "up");

View file

@ -25,6 +25,7 @@ layers_arrange(struct output *output)
wlr_output_effective_resolution(output->wlr_output, wlr_output_effective_resolution(output->wlr_output,
&full_area.width, &full_area.height); &full_area.width, &full_area.height);
struct wlr_box usable_area = full_area; struct wlr_box usable_area = full_area;
struct wlr_box old_usable_area = output->usable_area;
struct server *server = output->server; struct server *server = output->server;
struct wlr_scene_output *scene_output = struct wlr_scene_output *scene_output =
@ -37,11 +38,29 @@ layers_arrange(struct output *output)
int nr_layers = sizeof(output->layers) / sizeof(output->layers[0]); int nr_layers = sizeof(output->layers) / sizeof(output->layers[0]);
for (int i = 0; i < nr_layers; i++) { for (int i = 0; i < nr_layers; i++) {
struct lab_layer_surface *lab_layer_surface; struct lab_layer_surface *lab_layer_surface;
/*
* First we go over the list of surfaces that have
* exclusive_zone set (e.g. statusbars) because we have to
* determine the usable area before processing regular layouts.
*/
wl_list_for_each(lab_layer_surface, &output->layers[i], link) { wl_list_for_each(lab_layer_surface, &output->layers[i], link) {
struct wlr_scene_layer_surface_v1 *scene_layer_surface = struct wlr_scene_layer_surface_v1 *scene_layer_surface =
lab_layer_surface->scene_layer_surface; lab_layer_surface->scene_layer_surface;
wlr_scene_layer_surface_v1_configure( if (scene_layer_surface->layer_surface->current.exclusive_zone) {
scene_layer_surface, &full_area, &usable_area); wlr_scene_layer_surface_v1_configure(
scene_layer_surface, &full_area, &usable_area);
}
}
/* Now we process regular layouts */
wl_list_for_each(lab_layer_surface, &output->layers[i], link) {
struct wlr_scene_layer_surface_v1 *scene_layer_surface =
lab_layer_surface->scene_layer_surface;
if (!scene_layer_surface->layer_surface->current.exclusive_zone) {
wlr_scene_layer_surface_v1_configure(
scene_layer_surface, &full_area, &usable_area);
}
} }
wlr_scene_node_set_position(&output->layer_tree[i]->node, wlr_scene_node_set_position(&output->layer_tree[i]->node,
@ -80,7 +99,12 @@ layers_arrange(struct output *output)
!seat->focused_layer->current.keyboard_interactive) { !seat->focused_layer->current.keyboard_interactive) {
seat_set_focus_layer(seat, NULL); seat_set_focus_layer(seat, NULL);
} }
/* FIXME: should we call a desktop_arrange_all_views() here? */
/* Finally re-arrange all views based on usable_area */
if (old_usable_area.width != output->usable_area.width
|| old_usable_area.height != output->usable_area.height) {
desktop_arrange_all_views(server);
}
} }
static void static void
@ -350,7 +374,7 @@ new_layer_surface_notify(struct wl_listener *listener, void *data)
return; return;
} }
wl_list_insert(&output->layers[layer_surface->pending.layer], wl_list_insert(output->layers[layer_surface->pending.layer].prev,
&surface->link); &surface->link);
/* /*
* Temporarily set the layer's current state to pending so that * Temporarily set the layer's current state to pending so that

View file

@ -238,16 +238,16 @@ fill_item(char *nodename, char *content)
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. " wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
"nodename: '%s' content: '%s'", nodename, content); "nodename: '%s' content: '%s'", nodename, content);
} else if (!strcmp(nodename, "command.action")) { } else if (!strcmp(nodename, "command.action")) {
current_item_action->arg = strdup(content); action_arg_add_str(current_item_action, NULL, content);
} else if (!strcmp(nodename, "execute.action")) { } else if (!strcmp(nodename, "execute.action")) {
/* /*
* <action name="Execute"><execute>foo</execute></action> * <action name="Execute"><execute>foo</execute></action>
* is deprecated, but we support it anyway for backward * is deprecated, but we support it anyway for backward
* compatibility with old openbox-menu generators * compatibility with old openbox-menu generators
*/ */
current_item_action->arg = strdup(content); action_arg_add_str(current_item_action, NULL, content);
} else if (!strcmp(nodename, "to.action")) { } else if (!strcmp(nodename, "to.action")) {
current_item_action->arg = strdup(content); action_arg_add_str(current_item_action, NULL, content);
} }
} }

View file

@ -209,10 +209,7 @@ static void
output_update_for_layout_change(struct server *server) output_update_for_layout_change(struct server *server)
{ {
/* Adjust window positions/sizes */ /* Adjust window positions/sizes */
struct view *view; desktop_arrange_all_views(server);
wl_list_for_each(view, &server->views, link) {
view_adjust_for_layout_change(view);
}
/* /*
* "Move" each wlr_output_cursor (in per-output coordinates) to * "Move" each wlr_output_cursor (in per-output coordinates) to

View file

@ -42,7 +42,6 @@ reload_config_and_theme(void)
if (!view->mapped || !view->ssd.enabled) { if (!view->mapped || !view->ssd.enabled) {
continue; continue;
} }
view->margin = ssd_thickness(view);
ssd_reload(view); ssd_reload(view);
} }

View file

@ -17,6 +17,10 @@
struct border struct border
ssd_thickness(struct view *view) ssd_thickness(struct view *view)
{ {
if (!view->ssd.enabled) {
struct border border = { 0 };
return border;
}
struct theme *theme = view->server->theme; struct theme *theme = view->server->theme;
struct border border = { struct border border = {
.top = theme->title_height + theme->border_width, .top = theme->title_height + theme->border_width,
@ -159,6 +163,7 @@ ssd_create(struct view *view)
ssd_extents_create(view); ssd_extents_create(view);
ssd_border_create(view); ssd_border_create(view);
ssd_titlebar_create(view); ssd_titlebar_create(view);
view->margin = ssd_thickness(view);
} }
void void
@ -171,10 +176,12 @@ ssd_update_geometry(struct view *view)
if (!view->ssd.enabled) { if (!view->ssd.enabled) {
if (view->ssd.tree->node.enabled) { if (view->ssd.tree->node.enabled) {
wlr_scene_node_set_enabled(&view->ssd.tree->node, false); wlr_scene_node_set_enabled(&view->ssd.tree->node, false);
view->margin = ssd_thickness(view);
} }
return; return;
} else if (!view->ssd.tree->node.enabled) { } else if (!view->ssd.tree->node.enabled) {
wlr_scene_node_set_enabled(&view->ssd.tree->node, true); wlr_scene_node_set_enabled(&view->ssd.tree->node, true);
view->margin = ssd_thickness(view);
} }
int width = view->w; int width = view->w;
@ -198,15 +205,6 @@ ssd_update_geometry(struct view *view)
view->ssd.state.y = view->y; view->ssd.state.y = view->y;
} }
void
ssd_hide(struct view *view)
{
if (!view->ssd.tree) {
return;
}
wlr_scene_node_set_enabled(&view->ssd.tree->node, false);
}
void ssd_reload(struct view *view) void ssd_reload(struct view *view)
{ {
if (!view->ssd.tree) { if (!view->ssd.tree) {

View file

@ -8,8 +8,93 @@
#include "menu/menu.h" #include "menu/menu.h"
#include "workspaces.h" #include "workspaces.h"
#define LAB_FALLBACK_WIDTH 640
#define LAB_FALLBACK_HEIGHT 480
#define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b))
/**
* All view_apply_xxx_geometry() functions must *not* modify
* any state besides repositioning or resizing the view.
*
* They may be called repeatably during output layout changes.
*/
enum view_edge {
VIEW_EDGE_INVALID = 0,
VIEW_EDGE_LEFT,
VIEW_EDGE_RIGHT,
VIEW_EDGE_UP,
VIEW_EDGE_DOWN,
VIEW_EDGE_CENTER,
};
static enum view_edge
view_edge_invert(enum view_edge edge)
{
switch (edge) {
case VIEW_EDGE_LEFT:
return VIEW_EDGE_RIGHT;
case VIEW_EDGE_RIGHT:
return VIEW_EDGE_LEFT;
case VIEW_EDGE_UP:
return VIEW_EDGE_DOWN;
case VIEW_EDGE_DOWN:
return VIEW_EDGE_UP;
case VIEW_EDGE_CENTER:
case VIEW_EDGE_INVALID:
default:
return VIEW_EDGE_INVALID;
}
}
static struct wlr_box
view_get_edge_snap_box(struct view *view, struct output *output,
enum view_edge edge)
{
struct wlr_box usable = output_usable_area_in_layout_coords(output);
if (usable.height == output->wlr_output->height
&& output->wlr_output->scale != 1) {
usable.height /= output->wlr_output->scale;
}
if (usable.width == output->wlr_output->width
&& output->wlr_output->scale != 1) {
usable.width /= output->wlr_output->scale;
}
int x_offset = edge == VIEW_EDGE_RIGHT
? (usable.width + rc.gap) / 2 : rc.gap;
int y_offset = edge == VIEW_EDGE_DOWN
? (usable.height + rc.gap) / 2 : rc.gap;
int base_width, base_height;
switch (edge) {
case VIEW_EDGE_LEFT:
case VIEW_EDGE_RIGHT:
base_width = (usable.width - 3 * rc.gap) / 2;
base_height = usable.height - 2 * rc.gap;
break;
case VIEW_EDGE_UP:
case VIEW_EDGE_DOWN:
base_width = usable.width - 2 * rc.gap;
base_height = (usable.height - 3 * rc.gap) / 2;
break;
default:
case VIEW_EDGE_CENTER:
base_width = usable.width - 2 * rc.gap;
base_height = usable.height - 2 * rc.gap;
break;
}
struct wlr_box dst = {
.x = x_offset + usable.x + view->margin.left,
.y = y_offset + usable.y + view->margin.top,
.width = base_width - view->margin.left - view->margin.right,
.height = base_height - view->margin.top - view->margin.bottom,
};
return dst;
}
void void
view_set_activated(struct view *view, bool activated) view_set_activated(struct view *view, bool activated)
{ {
@ -176,6 +261,38 @@ view_compute_centered_position(struct view *view, int w, int h, int *x, int *y)
return true; return true;
} }
static void
set_fallback_geometry(struct view *view)
{
view->natural_geometry.width = LAB_FALLBACK_WIDTH;
view->natural_geometry.height = LAB_FALLBACK_HEIGHT;
view_compute_centered_position(view,
view->natural_geometry.width,
view->natural_geometry.height,
&view->natural_geometry.x,
&view->natural_geometry.y);
}
#undef LAB_FALLBACK_WIDTH
#undef LAB_FALLBACK_HEIGHT
static void
view_store_natural_geometry(struct view *view)
{
/**
* If an application was started maximized or fullscreened, its
* natural_geometry width/height may still be zero in which case we set
* some fallback values. This is the case with foot and Qt applications.
*/
if (!view->w || !view->h) {
set_fallback_geometry(view);
} else {
view->natural_geometry.x = view->x;
view->natural_geometry.y = view->y;
view->natural_geometry.width = view->w;
view->natural_geometry.height = view->h;
}
}
void void
view_center(struct view *view) view_center(struct view *view)
{ {
@ -185,6 +302,27 @@ view_center(struct view *view)
} }
} }
static void
view_apply_tiled_geometry(struct view *view, struct output *output)
{
assert(view->tiled);
if (!output) {
output = view_output(view);
}
if (!output) {
wlr_log(WLR_ERROR, "Can't tile: no output");
return;
}
struct wlr_box dst = view_get_edge_snap_box(view, output, view->tiled);
if (view->w == dst.width && view->h == dst.height) {
/* move horizontally/vertically without changing size */
view_move(view, dst.x, dst.y);
} else {
view_move_resize(view, dst);
}
}
static void static void
view_apply_fullscreen_geometry(struct view *view, struct wlr_output *wlr_output) view_apply_fullscreen_geometry(struct view *view, struct wlr_output *wlr_output)
{ {
@ -234,41 +372,17 @@ view_apply_maximized_geometry(struct view *view)
view_move_resize(view, box); view_move_resize(view, box);
} }
#define LAB_FALLBACK_WIDTH (640)
#define LAB_FALLBACK_HEIGHT (480)
static void
set_fallback_geometry(struct view *view)
{
view->unmaximized_geometry.width = LAB_FALLBACK_WIDTH;
view->unmaximized_geometry.height = LAB_FALLBACK_HEIGHT;
view_compute_centered_position(view,
view->unmaximized_geometry.width,
view->unmaximized_geometry.height,
&view->unmaximized_geometry.x,
&view->unmaximized_geometry.y);
}
static void static void
view_apply_unmaximized_geometry(struct view *view) view_apply_unmaximized_geometry(struct view *view)
{ {
/*
* If an application was started maximized, its unmaximized_geometry
* width/height may still be zero in which case we set some fallback
* values. This is the case with foot and Qt applications.
*/
if (wlr_box_empty(&view->unmaximized_geometry)) {
set_fallback_geometry(view);
}
struct wlr_output_layout *layout = view->server->output_layout; struct wlr_output_layout *layout = view->server->output_layout;
if (wlr_output_layout_intersects(layout, NULL, if (wlr_output_layout_intersects(layout, NULL,
&view->unmaximized_geometry)) { &view->natural_geometry)) {
/* restore to original geometry */ /* restore to original geometry */
view_move_resize(view, view->unmaximized_geometry); view_move_resize(view, view->natural_geometry);
} else { } else {
/* reposition if original geometry is offscreen */ /* reposition if original geometry is offscreen */
struct wlr_box box = view->unmaximized_geometry; struct wlr_box box = view->natural_geometry;
if (view_compute_centered_position(view, box.width, box.height, if (view_compute_centered_position(view, box.width, box.height,
&box.x, &box.y)) { &box.x, &box.y)) {
view_move_resize(view, box); view_move_resize(view, box);
@ -294,16 +408,18 @@ view_maximize(struct view *view, bool maximize)
} }
if (maximize) { if (maximize) {
interactive_end(view); interactive_end(view);
view->unmaximized_geometry.x = view->x; if (!view->tiled) {
view->unmaximized_geometry.y = view->y; view_store_natural_geometry(view);
view->unmaximized_geometry.width = view->w; }
view->unmaximized_geometry.height = view->h;
view_apply_maximized_geometry(view); view_apply_maximized_geometry(view);
view->maximized = true; view->maximized = true;
} else { } else {
/* unmaximize */ /* unmaximize */
view_apply_unmaximized_geometry(view); if (view->tiled) {
view_apply_tiled_geometry(view, NULL);
} else {
view_apply_unmaximized_geometry(view);
}
view->maximized = false; view->maximized = false;
} }
} }
@ -322,6 +438,8 @@ view_toggle_decorations(struct view *view)
ssd_update_geometry(view); ssd_update_geometry(view);
if (view->maximized) { if (view->maximized) {
view_apply_maximized_geometry(view); view_apply_maximized_geometry(view);
} else if (view->tiled) {
view_apply_tiled_geometry(view, NULL);
} }
} }
} }
@ -353,6 +471,8 @@ view_set_decorations(struct view *view, bool decorations)
ssd_update_geometry(view); ssd_update_geometry(view);
if (view->maximized) { if (view->maximized) {
view_apply_maximized_geometry(view); view_apply_maximized_geometry(view);
} else if (view->tiled) {
view_apply_tiled_geometry(view, NULL);
} }
} }
} }
@ -381,11 +501,8 @@ view_set_fullscreen(struct view *view, bool fullscreen,
wlr_output = view_wlr_output(view); wlr_output = view_wlr_output(view);
} }
if (fullscreen) { if (fullscreen) {
if (!view->maximized) { if (!view->maximized && !view->tiled) {
view->unmaximized_geometry.x = view->x; view_store_natural_geometry(view);
view->unmaximized_geometry.y = view->y;
view->unmaximized_geometry.width = view->w;
view->unmaximized_geometry.height = view->h;
} }
view->fullscreen = wlr_output; view->fullscreen = wlr_output;
view_apply_fullscreen_geometry(view, view->fullscreen); view_apply_fullscreen_geometry(view, view->fullscreen);
@ -393,6 +510,8 @@ view_set_fullscreen(struct view *view, bool fullscreen,
/* restore to normal */ /* restore to normal */
if (view->maximized) { if (view->maximized) {
view_apply_maximized_geometry(view); view_apply_maximized_geometry(view);
} else if (view->tiled) {
view_apply_tiled_geometry(view, NULL);
} else { } else {
view_apply_unmaximized_geometry(view); view_apply_unmaximized_geometry(view);
} }
@ -421,6 +540,9 @@ view_adjust_for_layout_change(struct view *view)
} else if (view->maximized) { } else if (view->maximized) {
/* recompute maximized geometry */ /* recompute maximized geometry */
view_apply_maximized_geometry(view); view_apply_maximized_geometry(view);
} else if (view->tiled) {
/* recompute tiled geometry */
view_apply_tiled_geometry(view, NULL);
} else { } else {
/* reposition view if it's offscreen */ /* reposition view if it's offscreen */
struct wlr_box box = { view->x, view->y, view->w, view->h }; struct wlr_box box = { view->x, view->y, view->w, view->h };
@ -515,35 +637,6 @@ view_move_to_edge(struct view *view, const char *direction)
view_move(view, x, y); view_move(view, x, y);
} }
enum view_edge {
VIEW_EDGE_INVALID,
VIEW_EDGE_LEFT,
VIEW_EDGE_RIGHT,
VIEW_EDGE_UP,
VIEW_EDGE_DOWN,
VIEW_EDGE_CENTER,
};
static enum view_edge
view_edge_invert(enum view_edge edge)
{
switch (edge) {
case VIEW_EDGE_LEFT:
return VIEW_EDGE_RIGHT;
case VIEW_EDGE_RIGHT:
return VIEW_EDGE_LEFT;
case VIEW_EDGE_UP:
return VIEW_EDGE_DOWN;
case VIEW_EDGE_DOWN:
return VIEW_EDGE_UP;
case VIEW_EDGE_CENTER:
case VIEW_EDGE_INVALID:
default:
return VIEW_EDGE_INVALID;
}
}
static enum view_edge static enum view_edge
view_edge_parse(const char *direction) view_edge_parse(const char *direction)
{ {
@ -565,53 +658,6 @@ view_edge_parse(const char *direction)
} }
} }
static struct wlr_box
view_get_edge_snap_box(struct view *view, struct output *output,
enum view_edge edge)
{
struct wlr_box usable = output_usable_area_in_layout_coords(output);
if (usable.height == output->wlr_output->height
&& output->wlr_output->scale != 1) {
usable.height /= output->wlr_output->scale;
}
if (usable.width == output->wlr_output->width
&& output->wlr_output->scale != 1) {
usable.width /= output->wlr_output->scale;
}
int x_offset = edge == VIEW_EDGE_RIGHT
? (usable.width + rc.gap) / 2 : rc.gap;
int y_offset = edge == VIEW_EDGE_DOWN
? (usable.height + rc.gap) / 2 : rc.gap;
int base_width, base_height;
switch (edge) {
case VIEW_EDGE_LEFT:
case VIEW_EDGE_RIGHT:
base_width = (usable.width - 3 * rc.gap) / 2;
base_height = usable.height - 2 * rc.gap;
break;
case VIEW_EDGE_UP:
case VIEW_EDGE_DOWN:
base_width = usable.width - 2 * rc.gap;
base_height = (usable.height - 3 * rc.gap) / 2;
break;
default:
case VIEW_EDGE_CENTER:
base_width = usable.width - 2 * rc.gap;
base_height = usable.height - 2 * rc.gap;
break;
}
struct wlr_box dst = {
.x = x_offset + usable.x + view->margin.left,
.y = y_offset + usable.y + view->margin.top,
.width = base_width - view->margin.left - view->margin.right,
.height = base_height - view->margin.top - view->margin.bottom,
};
return dst;
}
void void
view_snap_to_edge(struct view *view, const char *direction) view_snap_to_edge(struct view *view, const char *direction)
{ {
@ -619,6 +665,9 @@ view_snap_to_edge(struct view *view, const char *direction)
wlr_log(WLR_ERROR, "no view"); wlr_log(WLR_ERROR, "no view");
return; return;
} }
if (view->fullscreen) {
return;
}
struct output *output = view_output(view); struct output *output = view_output(view);
if (!output) { if (!output) {
wlr_log(WLR_ERROR, "no output"); wlr_log(WLR_ERROR, "no output");
@ -630,50 +679,50 @@ view_snap_to_edge(struct view *view, const char *direction)
return; return;
} }
struct wlr_box dst = view_get_edge_snap_box(view, output, edge); if (view->tiled == edge) {
/* We are already tiled for this edge and thus should switch outputs */
if (view->x == dst.x && view->y == dst.y && view->w == dst.width struct wlr_output *new_output = NULL;
&& view->h == dst.height) { struct wlr_output *current_output = output->wlr_output;
/* Move over to the next screen if this is already snapped. */ struct wlr_output_layout *layout = view->server->output_layout;
struct wlr_box usable =
output_usable_area_in_layout_coords(output);
switch (edge) { switch (edge) {
case VIEW_EDGE_LEFT: case VIEW_EDGE_LEFT:
dst.x -= (usable.width / 2) + 1; new_output = wlr_output_layout_adjacent_output(
layout, WLR_DIRECTION_LEFT, current_output, 1, 0);
break; break;
case VIEW_EDGE_RIGHT: case VIEW_EDGE_RIGHT:
dst.x += (usable.width / 2) + 1; new_output = wlr_output_layout_adjacent_output(
layout, WLR_DIRECTION_RIGHT, current_output, 1, 0);
break; break;
case VIEW_EDGE_UP: case VIEW_EDGE_UP:
dst.y -= (usable.height / 2) + 1; new_output = wlr_output_layout_adjacent_output(
layout, WLR_DIRECTION_UP, current_output, 0, 1);
break; break;
case VIEW_EDGE_DOWN: case VIEW_EDGE_DOWN:
dst.y += (usable.height / 2) + 1; new_output = wlr_output_layout_adjacent_output(
layout, WLR_DIRECTION_DOWN, current_output, 0, 1);
break; break;
default: default:
break; break;
} }
if (new_output && new_output != current_output) {
struct wlr_output *new_wlr_output = wlr_output_layout_output_at( /* Move to next output */
view->server->output_layout, dst.x, dst.y); edge = view_edge_invert(edge);
struct output *new_output = output = output_from_wlr_output(view->server, new_output);
output_from_wlr_output(view->server, new_wlr_output); } else {
/* No more output to move to */
if (new_output == output || !new_output
|| edge == VIEW_EDGE_CENTER) {
return; return;
} }
dst = view_get_edge_snap_box(view, new_output,
view_edge_invert(edge));
} }
if (view->w == dst.width && view->h == dst.height) { if (view->maximized) {
/* move horizontally/vertically without changing size */ /* Unmaximize + keep using existing natural_geometry */
view_move(view, dst.x, dst.y); view_maximize(view, false);
} else { } else if (!view->tiled) {
view_move_resize(view, dst); /* store current geometry as new natural_geometry */
view_store_natural_geometry(view);
} }
view->tiled = edge;
view_apply_tiled_geometry(view, output);
} }
const char * const char *

View file

@ -298,7 +298,6 @@ xdg_toplevel_view_map(struct view *view)
view->ssd.enabled = has_ssd(view); view->ssd.enabled = has_ssd(view);
if (view->ssd.enabled) { if (view->ssd.enabled) {
view->margin = ssd_thickness(view);
ssd_create(view); ssd_create(view);
} }
@ -311,7 +310,6 @@ xdg_toplevel_view_map(struct view *view)
} }
view_discover_output(view); view_discover_output(view);
view->been_mapped = true; view->been_mapped = true;
} }

View file

@ -20,6 +20,8 @@ unmanaged_handle_commit(struct wl_listener *listener, void *data)
struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface; struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface;
unmanaged->lx = xsurface->x; unmanaged->lx = xsurface->x;
unmanaged->ly = xsurface->y; unmanaged->ly = xsurface->y;
wlr_scene_node_set_position(unmanaged->node,
unmanaged->lx, unmanaged->ly);
} }
static void static void
@ -33,6 +35,8 @@ unmanaged_handle_set_geometry(struct wl_listener *listener, void *data)
wlr_log(WLR_DEBUG, "xwayland-unmanaged surface has moved"); wlr_log(WLR_DEBUG, "xwayland-unmanaged surface has moved");
unmanaged->lx = xsurface->x; unmanaged->lx = xsurface->x;
unmanaged->ly = xsurface->y; unmanaged->ly = xsurface->y;
wlr_scene_node_set_position(unmanaged->node,
unmanaged->lx, unmanaged->ly);
} }
} }
@ -59,10 +63,11 @@ unmanaged_handle_map(struct wl_listener *listener, void *data)
} }
/* node will be destroyed automatically once surface is destroyed */ /* node will be destroyed automatically once surface is destroyed */
struct wlr_scene_node *node = &wlr_scene_surface_create( unmanaged->node = &wlr_scene_surface_create(
unmanaged->server->unmanaged_tree, unmanaged->server->unmanaged_tree,
xsurface->surface)->buffer->node; xsurface->surface)->buffer->node;
wlr_scene_node_set_position(node, unmanaged->lx, unmanaged->ly); wlr_scene_node_set_position(unmanaged->node,
unmanaged->lx, unmanaged->ly);
} }
static void static void

View file

@ -144,6 +144,14 @@ handle_request_configure(struct wl_listener *listener, void *data)
int height = event->height; int height = event->height;
view_adjust_size(view, &width, &height); view_adjust_size(view, &width, &height);
view->pending_move_resize.update_x = event->x != view->x;
view->pending_move_resize.update_y = event->y != view->y;
view->pending_move_resize.x = event->x;
view->pending_move_resize.y = event->y;
view->pending_move_resize.width = width;
view->pending_move_resize.height = height;
wlr_scene_node_set_position(&view->scene_tree->node, event->x, event->y);
wlr_xwayland_surface_configure(view->xwayland_surface, wlr_xwayland_surface_configure(view->xwayland_surface,
event->x, event->y, width, height); event->x, event->y, width, height);
} }
@ -216,6 +224,13 @@ move(struct view *view, double x, double y)
{ {
view->x = x; view->x = x;
view->y = y; view->y = y;
/* override any previous pending move */
view->pending_move_resize.update_x = false;
view->pending_move_resize.update_y = false;
view->pending_move_resize.x = x;
view->pending_move_resize.y = y;
struct wlr_xwayland_surface *s = view->xwayland_surface; struct wlr_xwayland_surface *s = view->xwayland_surface;
wlr_xwayland_surface_configure(s, (int16_t)x, (int16_t)y, wlr_xwayland_surface_configure(s, (int16_t)x, (int16_t)y,
(uint16_t)s->width, (uint16_t)s->height); (uint16_t)s->width, (uint16_t)s->height);
@ -329,7 +344,7 @@ map(struct view *view)
if (!view->been_mapped) { if (!view->been_mapped) {
view->ssd.enabled = want_deco(view); view->ssd.enabled = want_deco(view);
if (view->ssd.enabled) { if (view->ssd.enabled) {
view->margin = ssd_thickness(view); ssd_create(view);
} }
if (!view->maximized && !view->fullscreen) { if (!view->maximized && !view->fullscreen) {
@ -341,11 +356,6 @@ map(struct view *view)
} }
view_discover_output(view); view_discover_output(view);
if (view->ssd.enabled) {
/* Create ssd after view_disover_output() had been called */
ssd_create(view);
}
view->been_mapped = true; view->been_mapped = true;
} }