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
#define __LABWC_ACTION_H
struct server;
struct view;
struct server;
struct wl_list;
struct action {
uint32_t type;
char *arg;
struct wl_list link;
struct wl_list link; /* struct keybinding.actions,
* struct mousebinding.actions,
* 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);
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,
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;
xkb_keysym_t *keysyms;
size_t keysyms_len;
struct wl_list actions;
struct wl_list link;
struct wl_list actions; /* struct action.link */
struct wl_list link; /* struct rcxml.keybinds */
};
/**

View file

@ -26,9 +26,9 @@ struct mousebind {
/* ex: doubleclick, press, drag */
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 */
};

View file

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

View file

@ -306,13 +306,14 @@ struct view {
bool been_mapped;
bool minimized;
bool maximized;
uint32_t tiled; /* private, enum view_edge in src/view.c */
struct wlr_output *fullscreen;
/* geometry of the wlr_surface contained within the view */
int x, y, w, h;
/* geometry before maximize */
struct wlr_box unmaximized_geometry;
/* user defined geometry before maximize / tiling / fullscreen */
struct wlr_box natural_geometry;
/*
* margin refers to the space between the extremities of the
@ -364,6 +365,7 @@ struct view {
struct xwayland_unmanaged {
struct server *server;
struct wlr_xwayland_surface *xwayland_surface;
struct wlr_scene_node *node;
struct wl_list link;
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_back(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 {
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 */
void ssd_create(struct view *view);
void ssd_hide(struct view *view);
void ssd_set_active(struct view *view, bool activated);
void ssd_update_title(struct view *view);
void ssd_update_geometry(struct view *view);
void ssd_reload(struct view *view);

View file

@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <signal.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <wlr/util/log.h>
@ -9,8 +11,8 @@
#include "debug.h"
#include "labwc.h"
#include "menu/menu.h"
#include "private/action.h"
#include "ssd.h"
#include "action.h"
#include "workspaces.h"
enum action_type {
@ -64,6 +66,24 @@ const char *action_names[] = {
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
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));
action->type = action_type_from_str(action_name);
wl_list_init(&action->args);
return action;
}
void action_list_free(struct wl_list *action_list)
{
struct action_arg *arg, *arg_tmp;
struct action *action, *action_tmp;
/* Free actions */
wl_list_for_each_safe(action, action_tmp, action_list, 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);
}
}
@ -151,9 +182,13 @@ actions_run(struct view *activator, struct server *server,
struct view *view;
struct action *action;
struct action_arg *arg;
wl_list_for_each(action, actions, link) {
wlr_log(WLR_DEBUG, "Handling action %s (%u) with arg %s",
action_names[action->type], action->type, action->arg);
wlr_log(WLR_DEBUG, "Handling action %s (%u)",
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
@ -171,28 +206,30 @@ actions_run(struct view *activator, struct server *server,
debug_dump_scene(server);
break;
case ACTION_TYPE_EXECUTE:
{
struct buf cmd;
buf_init(&cmd);
buf_add(&cmd, action->arg);
buf_expand_shell_variables(&cmd);
spawn_async_no_shell(cmd.buf);
free(cmd.buf);
if (!arg) {
wlr_log(WLR_ERROR, "Missing argument for Execute");
break;
}
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;
case ACTION_TYPE_EXIT:
wl_display_terminate(server->wl_display);
break;
case ACTION_TYPE_MOVE_TO_EDGE:
if (action->arg) {
view_move_to_edge(view, action->arg);
if (arg) {
view_move_to_edge(view, action_str_from_arg(arg));
} else {
wlr_log(WLR_ERROR, "Missing argument for MoveToEdge");
}
break;
case ACTION_TYPE_SNAP_TO_EDGE:
if (action->arg) {
view_snap_to_edge(view, action->arg);
if (arg) {
view_snap_to_edge(view, action_str_from_arg(arg));
} else {
wlr_log(WLR_ERROR, "Missing argument for SnapToEdge");
}
@ -211,7 +248,11 @@ actions_run(struct view *activator, struct server *server,
kill(getpid(), SIGHUP);
break;
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;
case ACTION_TYPE_TOGGLE_MAXIMIZE:
if (view) {
@ -263,27 +304,33 @@ actions_run(struct view *activator, struct server *server,
}
break;
case ACTION_TYPE_GO_TO_DESKTOP:
{
struct workspace *target;
target = workspaces_find(server->workspace_current, action->arg);
if (target) {
workspaces_switch_to(target);
}
if (!arg) {
wlr_log(WLR_ERROR, "Missing argument for GoToDesktop");
break;
}
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;
case ACTION_TYPE_SEND_TO_DESKTOP:
if (!arg) {
wlr_log(WLR_ERROR, "Missing argument for SendToDesktop");
break;
}
if (view) {
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) {
workspaces_send_to(view, target);
}
}
break;
case ACTION_TYPE_NONE:
wlr_log(WLR_ERROR,
"Not executing unknown action with arg %s",
action->arg);
wlr_log(WLR_ERROR, "Not executing unknown action");
break;
default:
/*
@ -292,9 +339,21 @@ actions_run(struct view *activator, struct server *server,
* adding a new action without installing a handler here.
*/
wlr_log(WLR_ERROR,
"Not executing invalid action (%u) with arg %s"
" This is a BUG. Please report.",
action->type, action->arg);
"Not executing invalid action (%u)"
" This is a BUG. Please report.", action->type);
}
}
}
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) {
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
"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")) {
/* Execute */
current_keybind_action->arg = strdup(content);
action_arg_add_str(current_keybind_action, NULL, content);
} else if (!strcmp(nodename, "direction.action")) {
/* MoveToEdge, SnapToEdge */
current_keybind_action->arg = strdup(content);
action_arg_add_str(current_keybind_action, NULL, content);
} else if (!strcmp(nodename, "menu.action")) {
/* ShowMenu */
current_keybind_action->arg = strdup(content);
action_arg_add_str(current_keybind_action, NULL, content);
} else if (!strcmp(nodename, "to.action")) {
/* 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) {
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
"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")) {
current_mousebind_action->arg = strdup(content);
action_arg_add_str(current_mousebind_action, NULL, content);
} 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")) {
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);
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);
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
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
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) {
/* Exit maximized or tiled mode */
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,
view->y, view->h, view->unmaximized_geometry.height);
view->unmaximized_geometry.x = new_x;
view->unmaximized_geometry.y = new_y;
view_maximize(view, false);
/*
* view_maximize() 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->y, view->h, view->natural_geometry.height);
view->natural_geometry.x = new_x;
view->natural_geometry.y = new_y;
if (view->maximized) {
view_maximize(view, false);
}
if (view->tiled) {
view_move_resize(view, view->natural_geometry);
}
/**
* 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->y = new_y;
view->w = view->unmaximized_geometry.width;
view->h = view->unmaximized_geometry.height;
} else {
return;
view->w = view->natural_geometry.width;
view->h = view->natural_geometry.height;
}
}
/* Moving or resizing always resets tiled state */
view->tiled = 0;
/*
* This function sets up an interactive move or resize operation, where
* the compositor stops propagating pointer events to clients and
@ -101,9 +122,9 @@ interactive_end(struct view *view)
* When unmaximizing later on restore
* original position
*/
view->unmaximized_geometry.x =
view->natural_geometry.x =
view->server->grab_box.x;
view->unmaximized_geometry.y =
view->natural_geometry.y =
view->server->grab_box.y;
} else {
view_snap_to_edge(view, "up");

View file

@ -25,6 +25,7 @@ layers_arrange(struct output *output)
wlr_output_effective_resolution(output->wlr_output,
&full_area.width, &full_area.height);
struct wlr_box usable_area = full_area;
struct wlr_box old_usable_area = output->usable_area;
struct server *server = output->server;
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]);
for (int i = 0; i < nr_layers; i++) {
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) {
struct wlr_scene_layer_surface_v1 *scene_layer_surface =
lab_layer_surface->scene_layer_surface;
wlr_scene_layer_surface_v1_configure(
scene_layer_surface, &full_area, &usable_area);
if (scene_layer_surface->layer_surface->current.exclusive_zone) {
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,
@ -80,7 +99,12 @@ layers_arrange(struct output *output)
!seat->focused_layer->current.keyboard_interactive) {
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
@ -350,7 +374,7 @@ new_layer_surface_notify(struct wl_listener *listener, void *data)
return;
}
wl_list_insert(&output->layers[layer_surface->pending.layer],
wl_list_insert(output->layers[layer_surface->pending.layer].prev,
&surface->link);
/*
* 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. "
"nodename: '%s' content: '%s'", nodename, content);
} 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")) {
/*
* <action name="Execute"><execute>foo</execute></action>
* is deprecated, but we support it anyway for backward
* 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")) {
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)
{
/* Adjust window positions/sizes */
struct view *view;
wl_list_for_each(view, &server->views, link) {
view_adjust_for_layout_change(view);
}
desktop_arrange_all_views(server);
/*
* "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) {
continue;
}
view->margin = ssd_thickness(view);
ssd_reload(view);
}

View file

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

View file

@ -8,8 +8,93 @@
#include "menu/menu.h"
#include "workspaces.h"
#define LAB_FALLBACK_WIDTH 640
#define LAB_FALLBACK_HEIGHT 480
#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
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;
}
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
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
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);
}
#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
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;
if (wlr_output_layout_intersects(layout, NULL,
&view->unmaximized_geometry)) {
&view->natural_geometry)) {
/* restore to original geometry */
view_move_resize(view, view->unmaximized_geometry);
view_move_resize(view, view->natural_geometry);
} else {
/* 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,
&box.x, &box.y)) {
view_move_resize(view, box);
@ -294,16 +408,18 @@ view_maximize(struct view *view, bool maximize)
}
if (maximize) {
interactive_end(view);
view->unmaximized_geometry.x = view->x;
view->unmaximized_geometry.y = view->y;
view->unmaximized_geometry.width = view->w;
view->unmaximized_geometry.height = view->h;
if (!view->tiled) {
view_store_natural_geometry(view);
}
view_apply_maximized_geometry(view);
view->maximized = true;
} else {
/* unmaximize */
view_apply_unmaximized_geometry(view);
if (view->tiled) {
view_apply_tiled_geometry(view, NULL);
} else {
view_apply_unmaximized_geometry(view);
}
view->maximized = false;
}
}
@ -322,6 +438,8 @@ view_toggle_decorations(struct view *view)
ssd_update_geometry(view);
if (view->maximized) {
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);
if (view->maximized) {
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);
}
if (fullscreen) {
if (!view->maximized) {
view->unmaximized_geometry.x = view->x;
view->unmaximized_geometry.y = view->y;
view->unmaximized_geometry.width = view->w;
view->unmaximized_geometry.height = view->h;
if (!view->maximized && !view->tiled) {
view_store_natural_geometry(view);
}
view->fullscreen = wlr_output;
view_apply_fullscreen_geometry(view, view->fullscreen);
@ -393,6 +510,8 @@ view_set_fullscreen(struct view *view, bool fullscreen,
/* restore to normal */
if (view->maximized) {
view_apply_maximized_geometry(view);
} else if (view->tiled) {
view_apply_tiled_geometry(view, NULL);
} else {
view_apply_unmaximized_geometry(view);
}
@ -421,6 +540,9 @@ view_adjust_for_layout_change(struct view *view)
} else if (view->maximized) {
/* recompute maximized geometry */
view_apply_maximized_geometry(view);
} else if (view->tiled) {
/* recompute tiled geometry */
view_apply_tiled_geometry(view, NULL);
} else {
/* reposition view if it's offscreen */
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);
}
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
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
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");
return;
}
if (view->fullscreen) {
return;
}
struct output *output = view_output(view);
if (!output) {
wlr_log(WLR_ERROR, "no output");
@ -630,50 +679,50 @@ view_snap_to_edge(struct view *view, const char *direction)
return;
}
struct wlr_box dst = view_get_edge_snap_box(view, output, edge);
if (view->x == dst.x && view->y == dst.y && view->w == dst.width
&& view->h == dst.height) {
/* Move over to the next screen if this is already snapped. */
struct wlr_box usable =
output_usable_area_in_layout_coords(output);
if (view->tiled == edge) {
/* We are already tiled for this edge and thus should switch outputs */
struct wlr_output *new_output = NULL;
struct wlr_output *current_output = output->wlr_output;
struct wlr_output_layout *layout = view->server->output_layout;
switch (edge) {
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;
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;
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;
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;
default:
break;
}
struct wlr_output *new_wlr_output = wlr_output_layout_output_at(
view->server->output_layout, dst.x, dst.y);
struct output *new_output =
output_from_wlr_output(view->server, new_wlr_output);
if (new_output == output || !new_output
|| edge == VIEW_EDGE_CENTER) {
if (new_output && new_output != current_output) {
/* Move to next output */
edge = view_edge_invert(edge);
output = output_from_wlr_output(view->server, new_output);
} else {
/* No more output to move to */
return;
}
dst = view_get_edge_snap_box(view, new_output,
view_edge_invert(edge));
}
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);
if (view->maximized) {
/* Unmaximize + keep using existing natural_geometry */
view_maximize(view, false);
} else if (!view->tiled) {
/* store current geometry as new natural_geometry */
view_store_natural_geometry(view);
}
view->tiled = edge;
view_apply_tiled_geometry(view, output);
}
const char *

View file

@ -298,7 +298,6 @@ xdg_toplevel_view_map(struct view *view)
view->ssd.enabled = has_ssd(view);
if (view->ssd.enabled) {
view->margin = ssd_thickness(view);
ssd_create(view);
}
@ -311,7 +310,6 @@ xdg_toplevel_view_map(struct view *view)
}
view_discover_output(view);
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;
unmanaged->lx = xsurface->x;
unmanaged->ly = xsurface->y;
wlr_scene_node_set_position(unmanaged->node,
unmanaged->lx, unmanaged->ly);
}
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");
unmanaged->lx = xsurface->x;
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 */
struct wlr_scene_node *node = &wlr_scene_surface_create(
unmanaged->node = &wlr_scene_surface_create(
unmanaged->server->unmanaged_tree,
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

View file

@ -144,6 +144,14 @@ handle_request_configure(struct wl_listener *listener, void *data)
int height = event->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,
event->x, event->y, width, height);
}
@ -216,6 +224,13 @@ move(struct view *view, double x, double y)
{
view->x = x;
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;
wlr_xwayland_surface_configure(s, (int16_t)x, (int16_t)y,
(uint16_t)s->width, (uint16_t)s->height);
@ -329,7 +344,7 @@ map(struct view *view)
if (!view->been_mapped) {
view->ssd.enabled = want_deco(view);
if (view->ssd.enabled) {
view->margin = ssd_thickness(view);
ssd_create(view);
}
if (!view->maximized && !view->fullscreen) {
@ -341,11 +356,6 @@ map(struct view *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;
}