Merge pull request #264 from labwc/scene-graph

Use wlroots scene-graph API
This commit is contained in:
Johan Malm 2022-03-06 17:33:40 +00:00 committed by GitHub
commit c73628ced1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 2786 additions and 2838 deletions

View file

@ -14,7 +14,7 @@ struct action {
struct action *action_create(const char *action_name);
void action_list_free(struct wl_list *action_list);
void action(struct view *activator, struct server *server,
void actions_run(struct view *activator, struct server *server,
struct wl_list *actions, uint32_t resize_edges);
#endif

50
include/buffer.h Normal file
View file

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Based on wlroots/include/types/wlr_buffer.c
*
* Copyright (c) 2017, 2018 Drew DeVault
* Copyright (c) 2018-2021 Simon Ser, Simon Zeni
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __LABWC_BUFFER_H
#define __LABWC_BUFFER_H
#include <cairo.h>
#include <wlr/types/wlr_buffer.h>
struct lab_data_buffer {
struct wlr_buffer base;
cairo_t *cairo;
void *data;
uint32_t format;
size_t stride;
bool free_on_destroy;
};
/* Create a buffer which creates a new cairo CAIRO_FORMAT_ARGB32 surface */
struct lab_data_buffer *buffer_create_cairo(uint32_t width, uint32_t height,
float scale, bool free_on_destroy);
/* Create a buffer which wraps a given DRM_FORMAT_ARGB8888 pointer */
struct lab_data_buffer *buffer_create_wrap(void *pixel_data, uint32_t width,
uint32_t height, uint32_t stride, bool free_on_destroy);
#endif /* __LABWC_BUFFER_H */

View file

@ -2,9 +2,7 @@
#ifndef __LABWC_FONT_H
#define __LABWC_FONT_H
struct server;
struct wlr_texture;
struct wlr_box;
struct lab_data_buffer;
struct font {
char *name;
@ -18,16 +16,23 @@ struct font {
int font_height(struct font *font);
/**
* texture_create - Create ARGB8888 texture using pango
* @server: context (for wlr_renderer)
* @texture: texture pointer; existing pointer will be freed
* font_buffer_create - Create ARGB8888 lab_data_buffer using pango
* @buffer: buffer pointer
* @max_width: max allowable width; will be ellipsized if longer
* @text: text to be generated as texture
* @font: font description
* @color: foreground color in rgba format
*/
void font_texture_create(struct server *server, struct wlr_texture **texture,
int max_width, const char *text, struct font *font, float *color);
void font_buffer_create(struct lab_data_buffer **buffer, int max_width,
const char *text, struct font *font, float *color);
/**
* font_buffer_update - Wrapper around font_buffer_create
* Only difference is that if given buffer pointer is != NULL
* wlr_buffer_drop() will be called on the buffer.
*/
void font_buffer_update(struct lab_data_buffer **buffer, int max_width,
const char *text, struct font *font, float *color);
/**
* font_finish - free some font related resources

View file

@ -0,0 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <wlr/types/wlr_scene.h>
struct wlr_scene_rect *lab_wlr_scene_get_rect(struct wlr_scene_node *node);

View file

@ -2,8 +2,9 @@
#ifndef __LABWC_MOUSEBIND_H
#define __LABWC_MOUSEBIND_H
#include "ssd.h"
#include <wayland-util.h>
#include "ssd.h"
#include "config/keybind.h"
enum mouse_event {
MOUSE_ACTION_NONE = 0,

9
include/debug.h Normal file
View file

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __DEBUG_H
#define __DEBUG_H
struct server;
void debug_dump_scene(struct server *server);
#endif /* __DEBUG_H */

View file

@ -13,6 +13,7 @@
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
@ -24,15 +25,16 @@
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_pointer_gestures_v1.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
@ -111,6 +113,8 @@ struct seat {
struct wl_listener idle_inhibitor_create;
};
struct lab_data_buffer;
struct server {
struct wl_display *wl_display;
struct wlr_renderer *renderer;
@ -137,6 +141,7 @@ struct server {
struct wl_list unmanaged_surfaces;
struct seat seat;
struct wlr_scene *scene;
/* cursor interactive */
enum input_mode input_mode;
@ -145,6 +150,21 @@ struct server {
struct wlr_box grab_box;
uint32_t resize_edges;
/* SSD state */
struct view *ssd_focused_view;
struct ssd_hover_state ssd_hover_state;
/* Tree for all non-layer xdg/xwayland-shell surfaces */
struct wlr_scene_tree *view_tree;
#if HAVE_XWAYLAND
/* Tree for unmanaged xsurfaces without initialized view (usually popups) */
struct wlr_scene_tree *unmanaged_tree;
#endif
/* Tree for built in menu */
struct wlr_scene_tree *menu_tree;
/* Tree for built in OSD / app switcher */
struct wlr_scene_tree *osd_tree;
struct wl_list outputs;
struct wl_listener new_output;
struct wlr_output_layout *output_layout;
@ -164,23 +184,27 @@ struct server {
struct view *cycle_view;
struct theme *theme;
struct menu *rootmenu;
struct menu *windowmenu;
struct menu *menu_current;
};
#define LAB_NR_LAYERS (4)
struct output {
struct wl_list link; /* server::outputs */
struct server *server;
struct wlr_output *wlr_output;
struct wlr_output_damage *damage;
struct wl_list layers[4];
struct wlr_scene_output *scene_output;
struct wl_list layers[LAB_NR_LAYERS];
struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS];
struct wlr_scene_tree *layer_popup_tree;
struct wlr_box usable_area;
struct wlr_texture *osd;
struct lab_data_buffer *osd_buffer;
struct wl_listener destroy;
struct wl_listener damage_frame;
struct wl_listener damage_destroy;
struct wl_listener frame;
};
#undef LAB_NR_LAYERS
enum view_type {
LAB_XDG_SHELL_VIEW,
@ -192,10 +216,6 @@ enum view_type {
struct view_impl {
void (*configure)(struct view *view, struct wlr_box geo);
void (*close)(struct view *view);
void (*for_each_popup_surface)(struct view *view,
wlr_surface_iterator_func_t iterator, void *data);
void (*for_each_surface)(struct view *view,
wlr_surface_iterator_func_t iterator, void *data);
const char *(*get_string_prop)(struct view *view, const char *prop);
void (*map)(struct view *view);
void (*move)(struct view *view, double x, double y);
@ -226,6 +246,8 @@ struct view {
#endif
};
struct wlr_surface *surface;
struct wlr_scene_tree *scene_tree;
struct wlr_scene_node *scene_node;
bool mapped;
bool been_mapped;
@ -255,24 +277,14 @@ struct view {
*/
struct border padding;
struct {
struct view_pending_move_resize {
bool update_x, update_y;
double x, y;
uint32_t width, height;
uint32_t configure_serial;
} pending_move_resize;
struct {
bool enabled;
struct wl_list parts;
struct wlr_box box; /* remember geo so we know when to update */
} ssd;
/* The title is unique to each view, so we store these here */
struct {
struct wlr_texture *active;
struct wlr_texture *inactive;
} title;
struct ssd ssd;
struct wlr_foreign_toplevel_handle_v1 *toplevel_handle;
struct wl_listener toplevel_handle_request_maximize;
@ -296,7 +308,6 @@ struct view {
struct wl_listener set_app_id; /* class on xwayland */
struct wl_listener set_decorations; /* xwayland only */
struct wl_listener new_popup; /* xdg-shell only */
struct wl_listener new_subsurface; /* xdg-shell only */
};
#if HAVE_XWAYLAND
@ -314,29 +325,6 @@ struct xwayland_unmanaged {
};
#endif
struct view_child {
struct wlr_surface *surface;
struct view *parent;
struct wl_listener commit;
struct wl_listener new_subsurface;
};
struct view_subsurface {
struct view_child view_child;
struct wlr_subsurface *subsurface;
struct wl_listener destroy;
};
struct xdg_popup {
struct view_child view_child;
struct wlr_xdg_popup *wlr_popup;
struct wl_listener destroy;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener new_popup;
};
struct constraint {
struct seat *seat;
struct wlr_pointer_constraint_v1 *constraint;
@ -361,12 +349,6 @@ void xwayland_unmanaged_create(struct server *server,
struct wlr_xwayland_surface *xsurface);
#endif
void view_child_init(struct view_child *child, struct view *view,
struct wlr_surface *wlr_surface);
void view_child_finish(struct view_child *child);
void view_subsurface_create(struct view *view,
struct wlr_subsurface *wlr_subsurface);
void view_set_activated(struct view *view, bool activated);
void view_close(struct view *view);
struct border view_border(struct view *view);
@ -393,10 +375,6 @@ void view_toggle_decorations(struct view *view);
void view_set_decorations(struct view *view, bool decorations);
void view_toggle_fullscreen(struct view *view);
void view_adjust_for_layout_change(struct view *view);
void view_for_each_surface(struct view *view,
wlr_surface_iterator_func_t iterator, void *user_data);
void view_for_each_popup_surface(struct view *view,
wlr_surface_iterator_func_t iterator, void *data);
void view_discover_output(struct view *view);
void view_move_to_edge(struct view *view, const char *direction);
void view_snap_to_edge(struct view *view, const char *direction);
@ -444,11 +422,33 @@ void desktop_focus_topmost_mapped_view(struct server *server);
bool isfocusable(struct view *view);
/**
* desktop_surface_and_view_at - find view and surface at (lx, ly)
* Note: If surface points to layer-surface, view will be set to NULL
* desktop_node_and_view_at - find view and scene_node at (lx, ly)
*
* Behavior if node points to a surface:
* - If surface is a layer-surface, *view_area will be
* set to LAB_SSD_LAYER_SURFACE and view will be NULL.
*
* - If surface is a 'lost' unmanaged xsurface (one
* with a never-mapped parent view), *view_area will
* be set to LAB_SSD_UNMANAGED and view will be NULL.
*
* 'Lost' unmanaged xsurfaces are usually caused by
* X11 applications opening popups without setting
* the main window as parent. Example: VLC submenus.
*
* - Any other surface will cause *view_area be set to
* LAB_SSD_CLIENT and return the attached view.
*
* Behavior if node points to internal elements:
* - *view_area will be set to the appropiate enum value
* and view will be NULL if the node is not part of the SSD.
*
* If no node is found for the given layout coordinates,
* *view_area will be set to LAB_SSD_ROOT and view will be NULL.
*
*/
struct view *desktop_surface_and_view_at(struct server *server, double lx,
double ly, struct wlr_surface **surface, double *sx, double *sy,
struct view *desktop_node_and_view_at(struct server *server, double lx,
double ly, struct wlr_scene_node **scene_node, double *sx, double *sy,
enum ssd_part_type *view_area);
struct view *desktop_view_at_cursor(struct server *server);
@ -473,8 +473,6 @@ void cursor_finish(struct seat *seat);
void keyboard_init(struct seat *seat);
void keyboard_finish(struct seat *seat);
void arrange_layers(struct output *output);
void seat_init(struct server *server);
void seat_finish(struct server *server);
void seat_reconfigure(struct server *server);
@ -486,24 +484,18 @@ void interactive_begin(struct view *view, enum input_mode mode,
void interactive_end(struct view *view);
void output_init(struct server *server);
void output_damage_surface(struct output *output, struct wlr_surface *surface,
double lx, double ly, bool whole);
void scale_box(struct wlr_box *box, float scale);
void output_manager_init(struct server *server);
struct output *output_from_wlr_output(struct server *server,
struct wlr_output *wlr_output);
struct wlr_box output_usable_area_in_layout_coords(struct output *output);
struct wlr_box output_usable_area_from_cursor_coords(struct server *server);
void damage_all_outputs(struct server *server);
void damage_view_whole(struct view *view);
void damage_view_part(struct view *view);
void server_init(struct server *server);
void server_start(struct server *server);
void server_finish(struct server *server);
/* update onscreen display 'alt-tab' texture */
/* update onscreen display 'alt-tab' buffer */
void osd_finish(struct server *server);
void osd_update(struct server *server);
/*

View file

@ -2,19 +2,14 @@
#ifndef __LABWC_LAYERS_H
#define __LABWC_LAYERS_H
#include <wayland-server.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_layer_shell_v1.h>
struct server;
enum layer_parent {
LAYER_PARENT_LAYER,
LAYER_PARENT_POPUP,
};
struct output;
struct lab_layer_surface {
struct wlr_layer_surface_v1 *layer_surface;
struct wl_list link;
struct wl_list link; /* output::layers */
struct wlr_scene_layer_surface_v1 *scene_layer_surface;
struct wl_listener destroy;
struct wl_listener map;
@ -22,38 +17,26 @@ struct lab_layer_surface {
struct wl_listener surface_commit;
struct wl_listener output_destroy;
struct wl_listener new_popup;
struct wl_listener new_subsurface;
struct wlr_box geo;
bool mapped;
/* TODO: add extent and layer */
/* TODO: add extent? */
struct server *server;
};
struct lab_layer_popup {
struct wlr_xdg_popup *wlr_popup;
enum layer_parent parent_type;
union {
struct lab_layer_surface *parent_layer;
struct lab_layer_popup *parent_popup;
};
struct wl_listener map;
struct wl_listener unmap;
struct wlr_scene_node *scene_node;
/* To simplify moving popup nodes from the bottom to the top layer */
struct wlr_box output_toplevel_sx_box;
struct wl_listener destroy;
struct wl_listener commit;
struct wl_listener new_popup;
};
struct lab_layer_subsurface {
struct wlr_subsurface *wlr_subsurface;
struct lab_layer_surface *layer_surface;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener destroy;
struct wl_listener commit;
};
void layers_init(struct server *server);
void layers_arrange(struct output *output);
#endif /* __LABWC_LAYERS_H */

View file

@ -3,19 +3,30 @@
#define __LABWC_MENU_H
#include <wayland-server.h>
#include <wlr/render/wlr_renderer.h>
struct lab_data_buffer;
struct wlr_scene_node;
enum menu_align {
LAB_MENU_OPEN_AUTO = 0,
LAB_MENU_OPEN_LEFT = 1 << 0,
LAB_MENU_OPEN_RIGHT = 1 << 1,
LAB_MENU_OPEN_TOP = 1 << 2,
LAB_MENU_OPEN_BOTTOM = 1 << 3,
};
struct menu_scene {
struct lab_data_buffer *buffer;
struct wlr_scene_node *text;
struct wlr_scene_node *background;
};
struct menuitem {
struct wl_list actions;
struct menu *parent;
struct menu *submenu;
struct wlr_box box;
struct {
struct wlr_texture *active;
struct wlr_texture *inactive;
int offset_x;
int offset_y;
} texture;
bool selected;
struct menu_scene normal;
struct menu_scene selected;
struct wl_list link; /* menu::menuitems */
};
@ -23,27 +34,66 @@ struct menuitem {
struct menu {
char *id;
char *label;
bool visible;
int item_height;
struct menu *parent;
struct wlr_box box;
struct {
int width;
int height;
} size;
struct wl_list menuitems;
struct server *server;
struct {
struct menu *menu;
struct menuitem *item;
} selection;
struct wlr_scene_tree *scene_tree;
};
void menu_init_rootmenu(struct server *server);
void menu_init_windowmenu(struct server *server);
void menu_finish(void);
/* menu_move - move to position (x, y) */
void menu_move(struct menu *menu, int x, int y);
/**
* menu_get_by_id - get menu by id
*
* @id id string defined in menu.xml like "root-menu"
*/
struct menu *menu_get_by_id(const char *id);
/* menu_set_selected - select item at (x, y) */
void menu_set_selected(struct menu *menu, int x, int y);
/**
* menu_open - open menu on position (x, y)
*
* This function will close server->menu_current, open the
* new menu and assign @menu to server->menu_current.
*
* Additionally, server->input_mode wil be set to LAB_INPUT_STATE_MENU.
*/
void menu_open(struct menu *menu, int x, int y);
/* menu_action_selected - select item at (x, y) */
void menu_action_selected(struct server *server, struct menu *menu);
/**
* menu_process_cursor_motion
*
* - handles hover effects
* - may open/close submenus
*/
void menu_process_cursor_motion(struct wlr_scene_node *node);
/**
* menu_call_actions - call actions associated with a menu node
*
* If menuitem connected to @node does not just open a submenu:
* - associated actions will be called
* - server->menu_current will be closed
* - server->menu_current will be set to NULL
*
* Returns true if actions have actually been executed
*/
bool menu_call_actions(struct wlr_scene_node *node);
/* menu_close - close menu */
void menu_close(struct menu *menu);
/* menu_reconfigure - reload theme and content */
void menu_reconfigure(struct server *server, struct menu *menu);
void menu_reconfigure(struct server *server);
#endif /* __LABWC_MENU_H */

67
include/node.h Normal file
View file

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __LABWC_NODE_DESCRIPTOR_H
#define __LABWC_NODE_DESCRIPTOR_H
#include <wlr/types/wlr_scene.h>
struct view;
struct lab_layer_surface;
struct lab_layer_popup;
struct menuitem;
enum node_descriptor_type {
LAB_NODE_DESC_NODE = 0,
LAB_NODE_DESC_VIEW,
LAB_NODE_DESC_XDG_POPUP,
LAB_NODE_DESC_LAYER_SURFACE,
LAB_NODE_DESC_LAYER_POPUP,
LAB_NODE_DESC_MENUITEM,
LAB_NODE_DESC_TREE,
};
struct node_descriptor {
enum node_descriptor_type type;
void *data;
struct wl_listener destroy;
};
/**
* node_descriptor_create - create node descriptor for wlr_scene_node user_data
* @scene_node: wlr_scene_node to attached node_descriptor to
* @type: node descriptor type
* @data: struct to point to as follows:
* - LAB_NODE_DESC_VIEW struct view
* - LAB_NODE_DESC_XDG_POPUP struct view
* - LAB_NODE_DESC_LAYER_SURFACE struct lab_layer_surface
* - LAB_NODE_DESC_LAYER_POPUP struct lab_layer_popup
*/
void node_descriptor_create(struct wlr_scene_node *scene_node,
enum node_descriptor_type type, void *data);
/**
* node_view_from_node - return view struct from node
* @wlr_scene_node: wlr_scene_node from which to return data
*/
struct view *node_view_from_node(struct wlr_scene_node *wlr_scene_node);
/**
* node_lab_surface_from_node - return lab_layer_surface struct from node
* @wlr_scene_node: wlr_scene_node from which to return data
*/
struct lab_layer_surface *node_layer_surface_from_node(
struct wlr_scene_node *wlr_scene_node);
/**
* node_layer_popup_from_node - return lab_layer_popup struct from node
* @wlr_scene_node: wlr_scene_node from which to return data
*/
struct lab_layer_popup *node_layer_popup_from_node(
struct wlr_scene_node *wlr_scene_node);
/**
* node_menuitem_from_node - return menuitem struct from node
* @wlr_scene_node: wlr_scene_node from which to return data
*/
struct menuitem *node_menuitem_from_node(
struct wlr_scene_node *wlr_scene_node);
#endif /* __LABWC_NODE_DESCRIPTOR_H */

View file

@ -2,6 +2,22 @@
#ifndef __LABWC_SSD_H
#define __LABWC_SSD_H
#include "buffer.h"
#include <wlr/util/box.h>
#define SSD_HEIGHT 26 /* TODO: use theme->title_height */
#define BUTTON_COUNT 4
#define BUTTON_WIDTH 26
#define EXTENDED_AREA 20
#define FOR_EACH(tmp, ...) \
{ \
__typeof__(tmp) _x[] = { __VA_ARGS__, NULL }; \
size_t _i = 0; \
for ((tmp) = _x[_i]; _i < sizeof(_x) / sizeof(_x[0]) - 1; (tmp) = _x[++_i])
#define FOR_EACH_END }
/*
* Sequence these according to the order they should be processed for
* press and hover events. Bear in mind that some of their respective
@ -26,54 +42,144 @@ enum ssd_part_type {
LAB_SSD_CLIENT,
LAB_SSD_FRAME,
LAB_SSD_ROOT,
LAB_SSD_MENU,
LAB_SSD_OSD,
LAB_SSD_LAYER_SURFACE,
LAB_SSD_UNMANAGED,
LAB_SSD_END_MARKER
};
/*
* Defer including labwc.h because it is using enum ssd_part_type.
* This is an issue for headers like mousebind.h which only includes
* ssd.h but does not include labwc.h.
*/
#include "labwc.h"
/* Forward declare arguments */
struct view;
struct wl_list;
struct wlr_box;
struct wlr_scene_tree;
struct ssd_sub_tree {
struct wlr_scene_tree *tree;
struct wl_list parts; /* ssd_part::link */
};
struct ssd_state_title_width {
int width;
bool truncated;
};
struct ssd {
bool enabled;
struct wlr_scene_tree *tree;
/*
* Cache for current values.
* Used to detect actual changes so we
* don't update things we don't have to.
*/
struct {
int width;
int height;
struct ssd_state_title {
char *text;
struct ssd_state_title_width active;
struct ssd_state_title_width inactive;
} title;
} state;
/* An invisble area around the view which allows resizing */
struct ssd_sub_tree extents;
/* The top of the view, containing buttons, title, .. */
struct {
/* struct wlr_scene_tree *tree; unused for now */
struct ssd_sub_tree active;
struct ssd_sub_tree inactive;
} titlebar;
/* Borders allow resizing as well */
struct {
/* struct wlr_scene_tree *tree; unused for now */
struct ssd_sub_tree active;
struct ssd_sub_tree inactive;
} border;
};
struct ssd_part {
struct wlr_box box;
enum ssd_part_type type;
/*
* The texture pointers are often held in other places such as the
* theme struct, so here we use ** in order to keep the code
* simple and avoid updating pointers as textures change.
*/
struct {
struct wlr_texture **active;
struct wlr_texture **inactive;
} texture;
/* Buffer pointer. May be NULL */
struct lab_data_buffer *buffer;
/*
* If a part does not contain textures, it'll just be rendered as a
* rectangle with the following colors.
*/
struct {
float *active;
float *inactive;
} color;
/* This part represented in scene graph */
struct wlr_scene_node *node;
struct wl_list link;
};
struct view;
struct ssd_hover_state {
struct view *view;
enum ssd_part_type type;
struct wlr_scene_node *node;
};
/* Public SSD API */
void ssd_create(struct view *view);
void ssd_hide(struct view *view);
void ssd_set_active(struct view *view);
void ssd_update_title(struct view *view);
void ssd_update_geometry(struct view *view);
void ssd_reload(struct view *view);
void ssd_destroy(struct view *view);
/* Returns hover overlay node so it can be disabled later on */
struct wlr_scene_node *ssd_button_hover_enable(
struct view *view, enum ssd_part_type type);
/* Public SSD helpers */
enum ssd_part_type ssd_at(struct view *view, double lx, double ly);
enum ssd_part_type ssd_get_part_type(
struct view *view, struct wlr_scene_node *node);
uint32_t ssd_resize_edges(enum ssd_part_type type);
bool ssd_is_button(enum ssd_part_type type);
bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate);
/* SSD internal helpers to create various SSD elements */
/* TODO: Replace some common args with a struct */
struct ssd_part *add_scene_part(
struct wl_list *part_list, enum ssd_part_type type);
struct ssd_part *add_scene_rect(
struct wl_list *list, enum ssd_part_type type,
struct wlr_scene_node *parent, int width, int height, int x, int y,
float color[4]);
struct ssd_part *add_scene_buffer(
struct wl_list *list, enum ssd_part_type type,
struct wlr_scene_node *parent, struct wlr_buffer *buffer, int x, int y);
struct ssd_part *add_scene_button(
struct wl_list *part_list, enum ssd_part_type type,
struct wlr_scene_node *parent, float *bg_color,
struct wlr_buffer *icon_buffer, int x);
struct ssd_part *add_scene_button_corner(
struct wl_list *part_list, enum ssd_part_type type,
struct wlr_scene_node *parent, struct wlr_buffer *corner_buffer,
struct wlr_buffer *icon_buffer, int x);
/* SSD internal helpers */
struct ssd_part *ssd_get_part(
struct wl_list *part_list, enum ssd_part_type type);
void ssd_destroy_parts(struct wl_list *list);
/* SSD internal */
void ssd_titlebar_create(struct view *view);
void ssd_titlebar_update(struct view *view);
void ssd_titlebar_destroy(struct view *view);
void ssd_border_create(struct view *view);
void ssd_border_update(struct view *view);
void ssd_border_destroy(struct view *view);
void ssd_extents_create(struct view *view);
void ssd_extents_update(struct view *view);
void ssd_extents_destroy(struct view *view);
/* TODO: clean up / update */
struct border ssd_thickness(struct view *view);
struct wlr_box ssd_max_extents(struct view *view);
struct wlr_box ssd_visible_box(struct view *view, enum ssd_part_type type);
enum ssd_part_type ssd_at(struct view *view, double lx, double ly);
uint32_t ssd_resize_edges(enum ssd_part_type type);
void ssd_update_title(struct view *view);
void ssd_create(struct view *view);
void ssd_destroy(struct view *view);
void ssd_update_geometry(struct view *view, bool force);
bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate);
bool ssd_is_button(enum ssd_part_type type);
#endif /* __LABWC_SSD_H */

View file

@ -54,20 +54,20 @@ struct theme {
float osd_label_text_color[4];
/* textures */
struct wlr_texture *xbm_close_active_unpressed;
struct wlr_texture *xbm_maximize_active_unpressed;
struct wlr_texture *xbm_iconify_active_unpressed;
struct wlr_texture *xbm_menu_active_unpressed;
struct lab_data_buffer *xbm_close_active_unpressed;
struct lab_data_buffer *xbm_maximize_active_unpressed;
struct lab_data_buffer *xbm_iconify_active_unpressed;
struct lab_data_buffer *xbm_menu_active_unpressed;
struct wlr_texture *xbm_close_inactive_unpressed;
struct wlr_texture *xbm_maximize_inactive_unpressed;
struct wlr_texture *xbm_iconify_inactive_unpressed;
struct wlr_texture *xbm_menu_inactive_unpressed;
struct lab_data_buffer *xbm_close_inactive_unpressed;
struct lab_data_buffer *xbm_maximize_inactive_unpressed;
struct lab_data_buffer *xbm_iconify_inactive_unpressed;
struct lab_data_buffer *xbm_menu_inactive_unpressed;
struct wlr_texture *corner_top_left_active_normal;
struct wlr_texture *corner_top_right_active_normal;
struct wlr_texture *corner_top_left_inactive_normal;
struct wlr_texture *corner_top_right_inactive_normal;
struct lab_data_buffer *corner_top_left_active_normal;
struct lab_data_buffer *corner_top_right_active_normal;
struct lab_data_buffer *corner_top_left_inactive_normal;
struct lab_data_buffer *corner_top_right_inactive_normal;
/* not set in rc.xml/themerc, but derived from font & padding_height */
int title_height;
@ -76,12 +76,10 @@ struct theme {
/**
* theme_init - read openbox theme and generate button textures
* @theme: theme data
* @renderer: wlr_renderer for creating button textures
* @theme_name: theme-name in <theme-dir>/<theme-name>/openbox-3/themerc
* Note <theme-dir> is obtained in theme-dir.c
*/
void theme_init(struct theme *theme, struct wlr_renderer *renderer,
const char *theme_name);
void theme_init(struct theme *theme, const char *theme_name);
/**
* theme_finish - free button textures

View file

@ -9,6 +9,6 @@
/**
* xbm_load - load theme xbm files into global theme struct
*/
void xbm_load(struct theme *theme, struct wlr_renderer *renderer);
void xbm_load(struct theme *theme);
#endif /* __LABWC_XBM_H */

View file

@ -30,14 +30,14 @@ add_project_arguments(cc.get_supported_arguments(
version='"@0@"'.format(meson.project_version())
git = find_program('git', native: true, required: false)
if git.found()
git_commit = run_command([git, 'describe', '--dirty'])
git_commit = run_command([git, 'describe', '--dirty'], check: false)
if git_commit.returncode() == 0
version = '"@0@"'.format(git_commit.stdout().strip())
endif
endif
add_project_arguments('-DLABWC_VERSION=@0@'.format(version), language: 'c')
wlroots_version = ['>=0.15.0', '<0.16.0']
wlroots_version = ['>=0.16.0', '<0.17.0']
wlroots_proj = subproject(
'wlroots',
default_options: ['default_library=static', 'examples=false'],

View file

@ -3,6 +3,7 @@
#include <wlr/util/log.h>
#include "common/spawn.h"
#include "common/zfree.h"
#include "debug.h"
#include "labwc.h"
#include "menu/menu.h"
#include "ssd.h"
@ -78,7 +79,8 @@ action_create(const char *action_name)
return action;
}
void action_list_free(struct wl_list *action_list) {
void action_list_free(struct wl_list *action_list)
{
struct action *action, *action_tmp;
wl_list_for_each_safe(action, action_tmp, action_list, link) {
wl_list_remove(&action->link);
@ -90,19 +92,15 @@ void action_list_free(struct wl_list *action_list) {
static void
show_menu(struct server *server, struct view *view, const char *menu_name)
{
struct menu *menu = NULL;
bool force_menu_top_left = false;
if (!menu_name) {
struct menu *menu = menu_get_by_id(menu_name);
if (!menu) {
return;
}
if (!strcasecmp(menu_name, "root-menu")) {
menu = server->rootmenu;
server->windowmenu->visible = false;
} else if (!strcasecmp(menu_name, "client-menu") && view) {
menu = server->windowmenu;
server->rootmenu->visible = false;
if (!strcasecmp(menu_name, "client-menu")) {
if (!view) {
return;
}
enum ssd_part_type type = ssd_at(view, server->seat.cursor->x,
server->seat.cursor->y);
if (type == LAB_SSD_BUTTON_WINDOW_MENU) {
@ -112,13 +110,8 @@ show_menu(struct server *server, struct view *view, const char *menu_name)
} else {
force_menu_top_left = true;
}
} else {
return;
}
menu->visible = true;
server->input_mode = LAB_INPUT_STATE_MENU;
int x, y;
if (force_menu_top_left) {
x = view->x;
@ -127,8 +120,7 @@ show_menu(struct server *server, struct view *view, const char *menu_name)
x = server->seat.cursor->x;
y = server->seat.cursor->y;
}
menu_move(menu, x, y);
damage_all_outputs(server);
menu_open(menu, x, y);
}
static struct view *
@ -138,7 +130,8 @@ activator_or_focused_view(struct view *activator, struct server *server)
}
void
action(struct view *activator, struct server *server, struct wl_list *actions, uint32_t resize_edges)
actions_run(struct view *activator, struct server *server,
struct wl_list *actions, uint32_t resize_edges)
{
if (!actions) {
wlr_log(WLR_ERROR, "empty actions");
@ -161,7 +154,7 @@ action(struct view *activator, struct server *server, struct wl_list *actions, u
}
break;
case ACTION_TYPE_DEBUG:
/* nothing */
debug_dump_scene(server);
break;
case ACTION_TYPE_EXECUTE:
{
@ -218,7 +211,6 @@ action(struct view *activator, struct server *server, struct wl_list *actions, u
view = desktop_view_at_cursor(server);
if (view) {
desktop_focus_and_activate_view(&server->seat, view);
damage_all_outputs(server);
}
break;
case ACTION_TYPE_ICONIFY:
@ -235,7 +227,6 @@ action(struct view *activator, struct server *server, struct wl_list *actions, u
case ACTION_TYPE_RAISE:
if (view) {
desktop_move_to_front(view);
damage_all_outputs(server);
}
break;
case ACTION_TYPE_RESIZE:

130
src/buffer.c Normal file
View file

@ -0,0 +1,130 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Based on wlroots/types/wlr_buffer.c
*
* Copyright (c) 2017, 2018 Drew DeVault
* Copyright (c) 2018-2021 Simon Ser, Simon Zeni
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include <assert.h>
#include <stdlib.h>
#include <drm_fourcc.h>
#include <wlr/interfaces/wlr_buffer.h>
#include "buffer.h"
static const struct wlr_buffer_impl data_buffer_impl;
static struct lab_data_buffer *
data_buffer_from_buffer(struct wlr_buffer *buffer)
{
assert(buffer->impl == &data_buffer_impl);
return (struct lab_data_buffer *)buffer;
}
static void
data_buffer_destroy(struct wlr_buffer *wlr_buffer)
{
struct lab_data_buffer *buffer = data_buffer_from_buffer(wlr_buffer);
if (!buffer->free_on_destroy) {
free(buffer);
return;
}
if (buffer->cairo) {
cairo_surface_t *surf = cairo_get_target(buffer->cairo);
cairo_destroy(buffer->cairo);
cairo_surface_destroy(surf);
} else if (buffer->data) {
free(buffer->data);
buffer->data = NULL;
}
free(buffer);
}
static bool
data_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags,
void **data, uint32_t *format, size_t *stride)
{
struct lab_data_buffer *buffer =
wl_container_of(wlr_buffer, buffer, base);
assert(buffer->data);
*data = (void *)buffer->data;
*format = buffer->format;
*stride = buffer->stride;
return true;
}
static void
data_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer)
{
/* noop */
}
static const struct wlr_buffer_impl data_buffer_impl = {
.destroy = data_buffer_destroy,
.begin_data_ptr_access = data_buffer_begin_data_ptr_access,
.end_data_ptr_access = data_buffer_end_data_ptr_access,
};
struct lab_data_buffer *
buffer_create_cairo(uint32_t width, uint32_t height, float scale,
bool free_on_destroy)
{
struct lab_data_buffer *buffer = calloc(1, sizeof(*buffer));
if (!buffer) {
return NULL;
}
wlr_buffer_init(&buffer->base, &data_buffer_impl, width, height);
cairo_surface_t *surf =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
cairo_surface_set_device_scale(surf, scale, scale);
buffer->cairo = cairo_create(surf);
buffer->data = cairo_image_surface_get_data(surf);
buffer->format = DRM_FORMAT_ARGB8888;
buffer->stride = cairo_image_surface_get_stride(surf);
buffer->free_on_destroy = free_on_destroy;
if (!buffer->data) {
cairo_destroy(buffer->cairo);
cairo_surface_destroy(surf);
free(buffer);
buffer = NULL;
}
return buffer;
}
struct lab_data_buffer *
buffer_create_wrap(void *pixel_data, uint32_t width, uint32_t height,
uint32_t stride, bool free_on_destroy)
{
struct lab_data_buffer *buffer = calloc(1, sizeof(*buffer));
if (!buffer) {
return NULL;
}
wlr_buffer_init(&buffer->base, &data_buffer_impl, width, height);
buffer->data = pixel_data;
buffer->format = DRM_FORMAT_ARGB8888;
buffer->stride = stride;
buffer->free_on_destroy = free_on_destroy;
return buffer;
}

View file

@ -7,6 +7,7 @@
#include <wlr/util/log.h>
#include "common/font.h"
#include "labwc.h"
#include "buffer.h"
static PangoRectangle
font_extents(struct font *font, const char *string)
@ -49,25 +50,33 @@ font_height(struct font *font)
}
void
font_texture_create(struct server *server, struct wlr_texture **texture,
int max_width, const char *text, struct font *font, float *color)
font_buffer_update(struct lab_data_buffer **buffer, int max_width,
const char *text, struct font *font, float *color)
{
if (*buffer) {
wlr_buffer_drop(&(*buffer)->base);
*buffer = NULL;
}
font_buffer_create(buffer, max_width, text, font, color);
}
void
font_buffer_create(struct lab_data_buffer **buffer, int max_width,
const char *text, struct font *font, float *color)
{
if (!text || !*text) {
return;
}
if (*texture) {
wlr_texture_destroy(*texture);
*texture = NULL;
}
PangoRectangle rect = font_extents(font, text);
if (max_width && rect.width > max_width) {
rect.width = max_width;
}
/* TODO: scale */
*buffer = buffer_create_cairo(rect.width, rect.height, 1, true);
cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
rect.width, rect.height);
cairo_t *cairo = cairo_create(surf);
cairo_t *cairo = (*buffer)->cairo;
cairo_surface_t *surf = cairo_get_target(cairo);
cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]);
cairo_move_to(cairo, 0, 0);
@ -88,13 +97,6 @@ font_texture_create(struct server *server, struct wlr_texture **texture,
g_object_unref(layout);
cairo_surface_flush(surf);
unsigned char *data = cairo_image_surface_get_data(surf);
*texture = wlr_texture_from_pixels(server->renderer, DRM_FORMAT_ARGB8888,
cairo_image_surface_get_stride(surf), rect.width,
rect.height, data);
cairo_destroy(cairo);
cairo_surface_destroy(surf);
}
void

View file

@ -4,6 +4,7 @@ labwc_sources += files(
'font.c',
'grab-file.c',
'nodename.c',
'scene-helpers.c',
'spawn.c',
'string-helpers.c',
'zfree.c',

View file

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <wlr/types/wlr_scene.h>
struct wlr_scene_rect *
lab_wlr_scene_get_rect(struct wlr_scene_node *node)
{
assert(node->type == WLR_SCENE_NODE_RECT);
return (struct wlr_scene_rect *)node;
}

View file

@ -12,15 +12,30 @@
#include "ssd.h"
#include "config/mousebind.h"
static bool
is_surface(enum ssd_part_type view_area)
{
return view_area == LAB_SSD_CLIENT
|| view_area == LAB_SSD_LAYER_SURFACE
#if HAVE_XWAYLAND
|| view_area == LAB_SSD_UNMANAGED
#endif
;
}
void
cursor_rebase(struct seat *seat, uint32_t time_msec)
{
double sx, sy;
struct wlr_surface *surface;
struct wlr_scene_node *node;
enum ssd_part_type view_area = LAB_SSD_NONE;
struct wlr_surface *surface = NULL;
desktop_surface_and_view_at(seat->server, seat->cursor->x,
seat->cursor->y, &surface, &sx, &sy, &view_area);
desktop_node_and_view_at(seat->server, seat->cursor->x,
seat->cursor->y, &node, &sx, &sy, &view_area);
if (is_surface(view_area)) {
surface = wlr_scene_surface_from_node(node)->surface;
}
if (surface) {
wlr_seat_pointer_notify_clear_focus(seat->seat);
@ -37,7 +52,7 @@ request_cursor_notify(struct wl_listener *listener, void *data)
{
struct seat *seat = wl_container_of(listener, seat, request_cursor);
/*
* This event is rasied by the seat when a client provides a cursor
* This event is raised by the seat when a client provides a cursor
* image
*/
struct wlr_seat_pointer_request_set_cursor_event *event = data;
@ -98,7 +113,6 @@ request_start_drag_notify(struct wl_listener *listener, void *data)
static void
process_cursor_move(struct server *server, uint32_t time)
{
damage_all_outputs(server);
double dx = server->seat.cursor->x - server->grab_x;
double dy = server->seat.cursor->y - server->grab_y;
struct view *view = server->grabbed_view;
@ -113,7 +127,6 @@ process_cursor_move(struct server *server, uint32_t time)
static void
process_cursor_resize(struct server *server, uint32_t time)
{
damage_all_outputs(server);
double dx = server->seat.cursor->x - server->grab_x;
double dy = server->seat.cursor->y - server->grab_y;
@ -170,28 +183,10 @@ input_inhibit_blocks_surface(struct seat *seat, struct wl_resource *resource)
inhibiting_client != wl_resource_get_client(resource);
}
static struct output *
get_output(struct server *server, struct wlr_cursor *cursor)
{
struct wlr_output *wlr_output = wlr_output_layout_output_at(
server->output_layout, cursor->x, cursor->y);
return output_from_wlr_output(server, wlr_output);
}
static void
damage_whole_current_output(struct server *server)
{
struct output *output = get_output(server, server->seat.cursor);
if (output) {
wlr_output_damage_add_whole(output->damage);
}
}
static void
process_cursor_motion(struct server *server, uint32_t time)
{
static bool cursor_name_set_by_server;
static enum ssd_part_type last_button_hover = LAB_SSD_NONE;
/* If the mode is non-passthrough, delegate to those functions. */
if (server->input_mode == LAB_INPUT_STATE_MOVE) {
@ -200,53 +195,47 @@ process_cursor_motion(struct server *server, uint32_t time)
} else if (server->input_mode == LAB_INPUT_STATE_RESIZE) {
process_cursor_resize(server, time);
return;
} else if (server->input_mode == LAB_INPUT_STATE_MENU) {
struct menu *menu = NULL;
if (server->rootmenu->visible) {
menu = server->rootmenu;
} else if (server->windowmenu->visible) {
menu = server->windowmenu;
} else {
return;
}
menu_set_selected(menu,
server->seat.cursor->x, server->seat.cursor->y);
damage_all_outputs(server);
return;
}
/* Otherwise, find view under the pointer and send the event along */
double sx, sy;
struct wlr_seat *wlr_seat = server->seat.seat;
struct wlr_surface *surface = NULL;
struct wlr_scene_node *node;
enum ssd_part_type view_area = LAB_SSD_NONE;
struct view *view = desktop_surface_and_view_at(server,
server->seat.cursor->x, server->seat.cursor->y, &surface,
struct view *view = desktop_node_and_view_at(server,
server->seat.cursor->x, server->seat.cursor->y, &node,
&sx, &sy, &view_area);
struct wlr_surface *surface = NULL;
if (is_surface(view_area)) {
surface = wlr_scene_surface_from_node(node)->surface;
}
/* resize handles */
uint32_t resize_edges = ssd_resize_edges(view_area);
/* Set cursor */
if (!view) {
/* root, etc. */
if (view_area == LAB_SSD_ROOT || view_area == LAB_SSD_MENU) {
cursor_set(&server->seat, XCURSOR_DEFAULT);
} else {
if (resize_edges) {
cursor_name_set_by_server = true;
cursor_set(&server->seat,
wlr_xcursor_get_resize_name(resize_edges));
} else if (ssd_part_contains(LAB_SSD_PART_TITLEBAR, view_area)) {
/* title and buttons */
cursor_set(&server->seat, XCURSOR_DEFAULT);
cursor_name_set_by_server = true;
} else if (cursor_name_set_by_server) {
/* window content */
cursor_set(&server->seat, XCURSOR_DEFAULT);
cursor_name_set_by_server = false;
}
} else if (resize_edges) {
cursor_name_set_by_server = true;
cursor_set(&server->seat,
wlr_xcursor_get_resize_name(resize_edges));
} else if (ssd_part_contains(LAB_SSD_PART_TITLEBAR, view_area)) {
/* title and buttons */
cursor_set(&server->seat, XCURSOR_DEFAULT);
cursor_name_set_by_server = true;
} else if (cursor_name_set_by_server) {
/* xdg/xwindow window content */
/* layershell or unmanaged */
cursor_set(&server->seat, XCURSOR_DEFAULT);
cursor_name_set_by_server = false;
}
if (view_area == LAB_SSD_MENU) {
menu_process_cursor_motion(node);
return;
}
if (view && rc.focus_follow_mouse) {
desktop_focus_and_activate_view(&server->seat, view);
@ -270,21 +259,28 @@ process_cursor_motion(struct server *server, uint32_t time)
}
mousebind->pressed_in_context = false;
action(NULL, server, &mousebind->actions, resize_edges);
actions_run(NULL, server, &mousebind->actions, resize_edges);
}
}
/* Required for iconify/maximize/close button mouse-over deco */
/* SSD button mouse-over */
struct ssd_hover_state *hover = &server->ssd_hover_state;
if (ssd_is_button(view_area)) {
if (last_button_hover != view_area) {
/* Cursor entered new button area */
damage_whole_current_output(server);
last_button_hover = view_area;
/* Cursor entered new button area */
if (hover->view != view || hover->type != view_area) {
if (hover->node) {
wlr_scene_node_set_enabled(hover->node, false);
}
hover->view = view;
hover->type = view_area;
hover->node = ssd_button_hover_enable(view, view_area);
}
} else if (last_button_hover != LAB_SSD_NONE) {
} else if (hover->node) {
/* Cursor left button area */
damage_whole_current_output(server);
last_button_hover = LAB_SSD_NONE;
wlr_scene_node_set_enabled(hover->node, false);
hover->view = NULL;
hover->type = LAB_SSD_NONE;
hover->node = NULL;
}
if (surface &&
@ -507,12 +503,19 @@ handle_release_mousebinding(struct view *view, struct server *server,
break;
}
continue;
case MOUSE_ACTION_DRAG:
if (mousebind->pressed_in_context) {
/* Swallow the release event as well as the press one */
activated_any = true;
activated_any_frame |= mousebind->context == LAB_SSD_FRAME;
}
continue;
default:
continue;
}
activated_any = true;
activated_any_frame |= mousebind->context == LAB_SSD_FRAME;
action(view, server, &mousebind->actions, resize_edges);
actions_run(view, server, &mousebind->actions, resize_edges);
}
}
/*
@ -576,6 +579,9 @@ handle_press_mousebinding(struct view *view, struct server *server,
* counted as a DOUBLECLICK.
*/
if (!double_click) {
/* Swallow the press event as well as the release one */
activated_any = true;
activated_any_frame |= mousebind->context == LAB_SSD_FRAME;
mousebind->pressed_in_context = true;
}
continue;
@ -591,7 +597,7 @@ handle_press_mousebinding(struct view *view, struct server *server,
}
activated_any = true;
activated_any_frame |= mousebind->context == LAB_SSD_FRAME;
action(view, server, &mousebind->actions, resize_edges);
actions_run(view, server, &mousebind->actions, resize_edges);
}
}
return activated_any && activated_any_frame;
@ -610,27 +616,48 @@ cursor_button(struct wl_listener *listener, void *data)
wlr_idle_notify_activity(seat->wlr_idle, seat->seat);
double sx, sy;
struct wlr_surface *surface;
struct wlr_scene_node *node;
enum ssd_part_type view_area = LAB_SSD_NONE;
uint32_t resize_edges;
uint32_t resize_edges = 0;
/**
* Used in WLR_BUTTON_RELEASED, set on WLR_BUTTON_PRESSED
*
* Automatically initialized with 0 / false and
* checkpatch.pl complains when done manually.
*/
static bool close_menu;
/* bindings to the Frame context swallow mouse events if activated */
bool triggered_frame_binding = false;
struct view *view = desktop_surface_and_view_at(server,
server->seat.cursor->x, server->seat.cursor->y, &surface,
struct view *view = desktop_node_and_view_at(server,
server->seat.cursor->x, server->seat.cursor->y, &node,
&sx, &sy, &view_area);
struct wlr_surface *surface = NULL;
if (is_surface(view_area)) {
surface = wlr_scene_surface_from_node(node)->surface;
}
/* get modifiers */
struct wlr_input_device *device = seat->keyboard_group->input_device;
uint32_t modifiers = wlr_keyboard_get_modifiers(device->keyboard);
struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard;
uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
/* handle _release_ */
if (event->state == WLR_BUTTON_RELEASED) {
if (server->input_mode == LAB_INPUT_STATE_MENU) {
if (close_menu) {
if (server->menu_current) {
menu_close(server->menu_current);
server->menu_current = NULL;
}
server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
cursor_rebase(&server->seat, event->time_msec);
close_menu = false;
}
return;
}
damage_all_outputs(server);
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
/* Exit interactive move/resize/menu mode. */
if (server->grabbed_view == view) {
@ -640,56 +667,41 @@ cursor_button(struct wl_listener *listener, void *data)
server->grabbed_view = NULL;
}
cursor_rebase(&server->seat, event->time_msec);
}
/* Handle _release_ on root window */
if (!view) {
handle_release_mousebinding(NULL, server, event->button,
modifiers, LAB_SSD_ROOT, 0);
return;
}
goto mousebindings;
}
/* Handle _press */
if (server->input_mode == LAB_INPUT_STATE_MENU) {
if (server->rootmenu->visible) {
menu_action_selected(server, server->rootmenu);
} else if (server->windowmenu->visible) {
menu_action_selected(server, server->windowmenu);
if (view_area != LAB_SSD_MENU) {
/* We close the menu on release so we don't leak a stray release */
close_menu = true;
} else if (menu_call_actions(node)) {
/* Action was successfull, may fail if item just opens a submenu */
close_menu = true;
}
server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
cursor_rebase(&server->seat, event->time_msec);
return;
}
/* Handle _press_ on a layer surface */
if (!view && surface) {
if (!wlr_surface_is_layer_surface(surface)) {
return;
}
if (view_area == LAB_SSD_LAYER_SURFACE) {
struct wlr_layer_surface_v1 *layer =
wlr_layer_surface_v1_from_wlr_surface(surface);
if (layer->current.keyboard_interactive) {
seat_set_focus_layer(&server->seat, layer);
}
wlr_seat_pointer_notify_button(seat->seat, event->time_msec,
event->button, event->state);
return;
}
/* Handle _press_ on root window */
if (!view) {
handle_press_mousebinding(NULL, server,
event->button, modifiers, LAB_SSD_ROOT, 0);
return;
}
/* Determine closest resize edges in case action is Resize */
resize_edges = ssd_resize_edges(view_area);
if (!resize_edges) {
resize_edges |= server->seat.cursor->x < view->x + view->w / 2
? WLR_EDGE_LEFT : WLR_EDGE_RIGHT;
resize_edges |= server->seat.cursor->y < view->y + view->h / 2
? WLR_EDGE_TOP : WLR_EDGE_BOTTOM;
if (view) {
/* Determine closest resize edges in case action is Resize */
resize_edges = ssd_resize_edges(view_area);
if (!resize_edges) {
resize_edges |= server->seat.cursor->x < view->x + view->w / 2
? WLR_EDGE_LEFT : WLR_EDGE_RIGHT;
resize_edges |= server->seat.cursor->y < view->y + view->h / 2
? WLR_EDGE_TOP : WLR_EDGE_BOTTOM;
}
}
mousebindings:
@ -702,7 +714,7 @@ mousebindings:
server, event->button, modifiers,
view_area, resize_edges);
}
if (!triggered_frame_binding) {
if (surface && !triggered_frame_binding) {
/* Notify client with pointer focus of button press */
wlr_seat_pointer_notify_button(seat->seat, event->time_msec,
event->button, event->state);

View file

@ -1,33 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "labwc.h"
void
damage_all_outputs(struct server *server)
{
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
if (output && output->wlr_output && output->damage) {
wlr_output_damage_add_whole(output->damage);
}
}
}
void
damage_view_part(struct view *view)
{
struct output *output;
wl_list_for_each (output, &view->server->outputs, link) {
output_damage_surface(output, view->surface, view->x, view->y,
false);
}
}
void
damage_view_whole(struct view *view)
{
struct output *output;
wl_list_for_each (output, &view->server->outputs, link) {
output_damage_surface(output, view->surface, view->x, view->y,
true);
}
}

181
src/debug.c Normal file
View file

@ -0,0 +1,181 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_scene.h>
#include "buffer.h"
#include "labwc.h"
#include "node.h"
#define HEADER_CHARS "------------------------------"
#define INDENT_SIZE 3
#define IGNORE_SSD true
#define IGNORE_MENU true
#define LEFT_COL_SPACE 35
static const char *
get_node_type(enum wlr_scene_node_type type)
{
switch (type) {
case WLR_SCENE_NODE_ROOT:
return "root";
case WLR_SCENE_NODE_TREE:
return "tree";
case WLR_SCENE_NODE_SURFACE:
return "surface";
case WLR_SCENE_NODE_RECT:
return "rect";
case WLR_SCENE_NODE_BUFFER:
return "buffer";
}
return "error";
}
static const char *
get_layer_name(uint32_t layer)
{
switch (layer) {
case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:
return "layer-background";
case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:
return "layer-bottom";
case ZWLR_LAYER_SHELL_V1_LAYER_TOP:
return "layer-top";
case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY:
return "layer-overlay";
default:
abort();
}
}
static const char *
get_view_part(struct view *view, struct wlr_scene_node *node)
{
if (view && node == &view->scene_tree->node) {
return "view";
}
if (view && node == view->scene_node) {
return "view->scene_node";
}
if (!view || !view->ssd.tree) {
return NULL;
}
if (node == &view->ssd.tree->node) {
return "view->ssd";
}
if (node == &view->ssd.titlebar.active.tree->node) {
return "titlebar.active";
}
if (node == &view->ssd.titlebar.inactive.tree->node) {
return "titlebar.inactive";
}
if (node == &view->ssd.border.active.tree->node) {
return "border.active";
}
if (node == &view->ssd.border.inactive.tree->node) {
return "border.inactive";
}
if (node == &view->ssd.extents.tree->node) {
return "extents";
}
return NULL;
}
static const char *
get_special(struct server *server, struct wlr_scene_node *node,
struct view **last_view)
{
if (node == &server->scene->node) {
return "server->scene";
}
if (node == &server->osd_tree->node) {
return "server->osd_tree";
}
if (node == &server->menu_tree->node) {
return "server->menu_tree";
}
if (node->parent == &server->scene->node) {
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
for (int i = 0; i < 4; i++) {
if (node == &output->layer_tree[i]->node) {
return get_layer_name(i);
}
}
}
}
#if HAVE_XWAYLAND
if (node == &server->unmanaged_tree->node) {
return "server->unmanaged_tree";
}
#endif
if (node == &server->view_tree->node) {
return "server->view_tree";
}
if (node->parent == &server->view_tree->node) {
*last_view = node_view_from_node(node);
}
const char *view_part = get_view_part(*last_view, node);
if (view_part) {
return view_part;
}
return get_node_type(node->type);
}
struct pad {
uint8_t left;
uint8_t right;
};
static struct pad
get_center_padding(const char *text, uint8_t max_width)
{
struct pad pad;
size_t text_len = strlen(text);
pad.left = (double)(max_width - text_len) / 2 + 0.5f;
pad.right = max_width - pad.left - text_len;
return pad;
}
static void
dump_tree(struct server *server, struct wlr_scene_node *node,
int pos, int x, int y)
{
static struct view *view;
const char *type = get_special(server, node, &view);
if (pos) {
printf("%*c+-- ", pos, ' ');
} else {
struct pad node_pad = get_center_padding("Node", 16);
printf(" %*c %4s %4s %*c%s\n", LEFT_COL_SPACE + 4, ' ',
"X", "Y", node_pad.left, ' ', "Node");
printf(" %*c %.4s %.4s %.16s\n", LEFT_COL_SPACE + 4, ' ',
HEADER_CHARS, HEADER_CHARS, HEADER_CHARS);
printf(" ");
}
int padding = LEFT_COL_SPACE - pos - strlen(type);
if (!pos) {
padding += 3;
}
printf("%s %*c %4d %4d [%p]\n", type, padding, ' ', x, y, node);
if ((IGNORE_MENU && node == &server->menu_tree->node)
|| (IGNORE_SSD && view && view->ssd.tree
&& node == &view->ssd.tree->node)) {
printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', "<skipping children>");
return;
}
struct wlr_scene_node *child;
wl_list_for_each(child, &node->state.children, state.link) {
dump_tree(server, child, pos + INDENT_SIZE,
x + child->state.x, y + child->state.y);
}
}
void
debug_dump_scene(struct server *server)
{
printf("\n");
dump_tree(server, &server->scene->node, 0, 0, 0);
printf("\n");
}

View file

@ -3,6 +3,7 @@
#include <assert.h>
#include "labwc.h"
#include "layers.h"
#include "node.h"
#include "ssd.h"
static void
@ -10,6 +11,7 @@ move_to_front(struct view *view)
{
wl_list_remove(&view->link);
wl_list_insert(&view->server->views, &view->link);
wlr_scene_node_raise_to_top(&view->scene_tree->node);
}
#if HAVE_XWAYLAND
@ -197,7 +199,6 @@ desktop_cycle_view(struct server *server, struct view *current,
view = wl_container_of(view->link.prev, view, link);
} while (&view->link == &server->views || !isfocusable(view));
}
damage_all_outputs(server);
return view;
}
@ -255,189 +256,85 @@ desktop_focus_topmost_mapped_view(struct server *server)
desktop_move_to_front(view);
}
static bool
_view_at(struct view *view, double lx, double ly, struct wlr_surface **surface,
double *sx, double *sy)
{
/*
* XDG toplevels may have nested surfaces, such as popup windows for
* context menus or tooltips. This function tests if any of those are
* underneath the coordinates lx and ly (in output Layout Coordinates).
* If so, it sets the surface pointer to that wlr_surface and the sx and
* sy coordinates to the coordinates relative to that surface's top-left
* corner.
*/
double view_sx = lx - view->x;
double view_sy = ly - view->y;
double _sx, _sy;
struct wlr_surface *_surface = NULL;
switch (view->type) {
case LAB_XDG_SHELL_VIEW:
_surface = wlr_xdg_surface_surface_at(
view->xdg_surface, view_sx, view_sy, &_sx, &_sy);
break;
#if HAVE_XWAYLAND
case LAB_XWAYLAND_VIEW:
_surface = wlr_surface_surface_at(view->surface, view_sx,
view_sy, &_sx, &_sy);
break;
#endif
}
if (_surface) {
*sx = _sx;
*sy = _sy;
*surface = _surface;
return true;
}
return false;
}
static struct
wlr_surface *layer_surface_at(struct wl_list *layer, double ox, double oy,
double *sx, double *sy)
{
struct lab_layer_surface *surface;
wl_list_for_each_reverse(surface, layer, link) {
double _sx = ox - surface->geo.x;
double _sy = oy - surface->geo.y;
struct wlr_surface *wlr_surface;
wlr_surface = wlr_layer_surface_v1_surface_at(
surface->layer_surface, _sx, _sy, sx, sy);
if (wlr_surface) {
return wlr_surface;
}
}
return NULL;
}
static bool
surface_is_xdg_popup(struct wlr_surface *surface)
{
if (wlr_surface_is_xdg_surface(surface)) {
struct wlr_xdg_surface *xdg_surface =
wlr_xdg_surface_from_wlr_surface(surface);
return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP;
}
return false;
}
static struct wlr_surface *
layer_surface_popup_at(struct output *output, struct wl_list *layer,
double ox, double oy, double *sx, double *sy)
{
struct lab_layer_surface *lab_layer;
wl_list_for_each_reverse(lab_layer, layer, link) {
double _sx = ox - lab_layer->geo.x;
double _sy = oy - lab_layer->geo.y;
struct wlr_surface *sub = wlr_layer_surface_v1_surface_at(
lab_layer->layer_surface, _sx, _sy, sx, sy);
if (sub && surface_is_xdg_popup(sub)) {
return sub;
}
}
return NULL;
}
struct view *
desktop_surface_and_view_at(struct server *server, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy,
desktop_node_and_view_at(struct server *server, double lx, double ly,
struct wlr_scene_node **scene_node, double *sx, double *sy,
enum ssd_part_type *view_area)
{
struct wlr_output *wlr_output = wlr_output_layout_output_at(
server->output_layout, lx, ly);
struct output *output = output_from_wlr_output(server, wlr_output);
struct wlr_scene_node *node =
wlr_scene_node_at(&server->scene->node, lx, ly, sx, sy);
if (!output) {
*scene_node = node;
if (!node) {
*view_area = LAB_SSD_ROOT;
return NULL;
}
double ox = lx, oy = ly;
wlr_output_layout_output_coords(output->server->output_layout,
wlr_output, &ox, &oy);
/* Overlay-layer */
*surface = layer_surface_at(
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
ox, oy, sx, sy);
if (*surface) {
return NULL;
}
/* Check all layer popups */
*surface = layer_surface_popup_at(output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
ox, oy, sx, sy);
if (*surface) {
return NULL;
}
*surface = layer_surface_popup_at(output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
ox, oy, sx, sy);
if (*surface) {
return NULL;
}
*surface = layer_surface_popup_at(output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
ox, oy, sx, sy);
if (*surface) {
return NULL;
}
/* Top-layer */
*surface = layer_surface_at(
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
ox, oy, sx, sy);
if (*surface) {
return NULL;
}
struct view *view;
wl_list_for_each (view, &server->views, link) {
if (!view->mapped) {
continue;
if (node->type == WLR_SCENE_NODE_SURFACE) {
struct wlr_surface *surface =
wlr_scene_surface_from_node(node)->surface;
if (wlr_surface_is_layer_surface(surface)) {
*view_area = LAB_SSD_LAYER_SURFACE;
return NULL;
}
/* This ignores server-side deco */
if (_view_at(view, lx, ly, surface, sx, sy)) {
*view_area = LAB_SSD_CLIENT;
return view;
#if HAVE_XWAYLAND
if (node->parent == &server->unmanaged_tree->node) {
*view_area = LAB_SSD_UNMANAGED;
return NULL;
}
if (!view->ssd.enabled) {
continue;
#endif
}
struct wlr_scene_node *osd = &server->osd_tree->node;
while (node) {
struct node_descriptor *desc = node->data;
/* TODO: convert to switch() */
if (desc) {
if (desc->type == LAB_NODE_DESC_VIEW) {
goto has_view_data;
}
if (desc->type == LAB_NODE_DESC_XDG_POPUP) {
goto has_view_data;
}
if (desc->type == LAB_NODE_DESC_LAYER_POPUP) {
/* FIXME: we shouldn't have to set *view_area */
*view_area = LAB_SSD_CLIENT;
return NULL;
}
if (desc->type == LAB_NODE_DESC_MENUITEM) {
/* Always return the top scene node for menu items */
*scene_node = node;
*view_area = LAB_SSD_MENU;
return NULL;
}
}
/* Now, let's deal with the server-side deco */
*view_area = ssd_at(view, lx, ly);
if (*view_area != LAB_SSD_NONE) {
return view;
if (node == osd) {
*view_area = LAB_SSD_OSD;
return NULL;
}
node = node->parent;
}
*surface = layer_surface_at(
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
ox, oy, sx, sy);
if (*surface) {
return NULL;
}
*surface = layer_surface_at(
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
ox, oy, sx, sy);
if (*surface) {
return NULL;
if (!node) {
wlr_log(WLR_ERROR, "Unknown node detected");
}
*view_area = LAB_SSD_NONE;
return NULL;
struct view *view;
struct node_descriptor *desc;
has_view_data:
desc = node->data;
view = desc->data;
*view_area = ssd_get_part_type(view, *scene_node);
return view;
}
struct view *
desktop_view_at_cursor(struct server *server)
{
double sx, sy;
struct wlr_surface *surface;
struct wlr_scene_node *node;
enum ssd_part_type view_area = LAB_SSD_NONE;
return desktop_surface_and_view_at(server,
return desktop_node_and_view_at(server,
server->seat.cursor->x, server->seat.cursor->y,
&surface, &sx, &sy, &view_area);
&node, &sx, &sy, &view_area);
}

View file

@ -2,6 +2,7 @@
#include <wlr/backend/multi.h>
#include <wlr/backend/session.h>
#include "action.h"
#include "buffer.h"
#include "key-state.h"
#include "labwc.h"
@ -40,15 +41,15 @@ keyboard_modifiers_notify(struct wl_listener *listener, void *data)
if (server->cycle_view) {
struct wlr_event_keyboard_key *event = data;
struct wlr_input_device *device = seat->keyboard_group->input_device;
damage_all_outputs(server);
struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard;
if ((event->state == WL_KEYBOARD_KEY_STATE_RELEASED)
&& !any_modifiers_pressed(device->keyboard)) {
&& !any_modifiers_pressed(keyboard)) {
/* end cycle */
desktop_focus_and_activate_view(&server->seat,
server->cycle_view);
desktop_move_to_front(server->cycle_view);
server->cycle_view = NULL;
osd_finish(server);
}
}
@ -68,7 +69,7 @@ handle_keybinding(struct server *server, uint32_t modifiers, xkb_keysym_t sym)
for (size_t i = 0; i < keybind->keysyms_len; i++) {
if (xkb_keysym_to_lower(sym) == keybind->keysyms[i]) {
wlr_keyboard_set_repeat_info(kb, 0, 0);
action(NULL, server, &keybind->actions, 0);
actions_run(NULL, server, &keybind->actions, 0);
return true;
}
}
@ -94,13 +95,13 @@ handle_compositor_keybindings(struct wl_listener *listener,
{
struct seat *seat = wl_container_of(listener, seat, keyboard_key);
struct server *server = seat->server;
struct wlr_input_device *device = seat->keyboard_group->input_device;
struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard;
/* Translate libinput keycode -> xkbcommon */
uint32_t keycode = event->keycode + 8;
/* Get a list of keysyms based on the keymap for this keyboard */
const xkb_keysym_t *syms;
int nsyms = xkb_state_key_get_syms(device->keyboard->xkb_state, keycode, &syms);
int nsyms = xkb_state_key_get_syms(keyboard->xkb_state, keycode, &syms);
bool handled = false;
@ -115,14 +116,13 @@ handle_compositor_keybindings(struct wl_listener *listener,
&& event->state == WL_KEYBOARD_KEY_STATE_RELEASED) {
int nr_bound_keys = key_state_bound_key_remove(keycode);
if (!nr_bound_keys) {
wlr_keyboard_set_repeat_info(device->keyboard,
wlr_keyboard_set_repeat_info(keyboard,
rc.repeat_rate, rc.repeat_delay);
}
return true;
}
uint32_t modifiers =
wlr_keyboard_get_modifiers(device->keyboard);
uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
/* Catch C-A-F1 to C-A-F12 to change tty */
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
@ -137,7 +137,6 @@ handle_compositor_keybindings(struct wl_listener *listener,
}
if (server->cycle_view) {
damage_all_outputs(server);
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
for (int i = 0; i < nsyms; i++) {
if (syms[i] == XKB_KEY_Escape) {
@ -188,7 +187,7 @@ keyboard_key_notify(struct wl_listener *listener, void *data)
struct server *server = seat->server;
struct wlr_event_keyboard_key *event = data;
struct wlr_seat *wlr_seat = server->seat.seat;
struct wlr_input_device *device = seat->keyboard_group->input_device;
struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard;
wlr_idle_notify_activity(seat->wlr_idle, seat->seat);
bool handled = false;
@ -199,7 +198,7 @@ keyboard_key_notify(struct wl_listener *listener, void *data)
}
if (!handled) {
wlr_seat_set_keyboard(wlr_seat, device);
wlr_seat_set_keyboard(wlr_seat, &keyboard->base);
wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
event->keycode, event->state);
}

View file

@ -2,7 +2,7 @@
/*
* layers.c - layer-shell implementation
*
* Based on:
* Based on
* - https://git.sr.ht/~sircmpwm/wio
* - https://github.com/swaywm/sway
* Copyright (C) 2019 Drew DeVault and Sway developers
@ -16,208 +16,54 @@
#include <wlr/util/log.h>
#include "layers.h"
#include "labwc.h"
static void
apply_exclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t exclusive,
int32_t margin_top, int32_t margin_right, int32_t margin_bottom,
int32_t margin_left)
{
if (exclusive <= 0) {
return;
}
struct {
uint32_t anchors;
int *positive_axis;
int *negative_axis;
int margin;
} edges[] = {
{
.anchors =
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP,
.positive_axis = &usable_area->y,
.negative_axis = &usable_area->height,
.margin = margin_top,
},
{
.anchors =
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
.positive_axis = NULL,
.negative_axis = &usable_area->height,
.margin = margin_bottom,
},
{
.anchors =
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
.positive_axis = &usable_area->x,
.negative_axis = &usable_area->width,
.margin = margin_left,
},
{
.anchors =
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
.positive_axis = NULL,
.negative_axis = &usable_area->width,
.margin = margin_right,
},
};
for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
if ((anchor & edges[i].anchors) == edges[i].anchors) {
if (edges[i].positive_axis) {
*edges[i].positive_axis +=
exclusive + edges[i].margin;
}
if (edges[i].negative_axis) {
*edges[i].negative_axis -=
exclusive + edges[i].margin;
}
}
}
}
/**
* @list: struct lab_layer_surface
*/
static void
arrange_layer(struct wlr_output *output, struct wl_list *list,
struct wlr_box *usable_area, bool exclusive)
{
struct lab_layer_surface *surface;
struct wlr_box full_area = { 0 };
wlr_output_effective_resolution(output, &full_area.width,
&full_area.height);
wl_list_for_each_reverse(surface, list, link) {
struct wlr_layer_surface_v1 *layer = surface->layer_surface;
struct wlr_layer_surface_v1_state *state = &layer->current;
if (exclusive != (state->exclusive_zone > 0)) {
continue;
}
struct wlr_box bounds;
if (state->exclusive_zone == -1) {
bounds = full_area;
} else {
bounds = *usable_area;
}
struct wlr_box box = {
.width = state->desired_width,
.height = state->desired_height
};
/* Horizontal axis */
const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
if ((state->anchor & both_horiz) && box.width == 0) {
box.x = bounds.x;
box.width = bounds.width;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
box.x = bounds.x;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
box.x = bounds.x + (bounds.width - box.width);
} else {
box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
}
/* Vertical axis */
const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
| ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
if ((state->anchor & both_vert) && box.height == 0) {
box.y = bounds.y;
box.height = bounds.height;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
box.y = bounds.y;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
box.y = bounds.y + (bounds.height - box.height);
} else {
box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
}
/* Margin */
if ((state->anchor & both_horiz) == both_horiz) {
box.x += state->margin.left;
box.width -= state->margin.left + state->margin.right;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
box.x += state->margin.left;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
box.x -= state->margin.right;
}
if ((state->anchor & both_vert) == both_vert) {
box.y += state->margin.top;
box.height -= state->margin.top + state->margin.bottom;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
box.y += state->margin.top;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
box.y -= state->margin.bottom;
}
if (box.width < 0 || box.height < 0) {
wlr_log(WLR_ERROR, "surface has no positive size");
continue;
}
/* Apply */
surface->geo = box;
apply_exclusive(usable_area, state->anchor,
state->exclusive_zone, state->margin.top,
state->margin.right, state->margin.bottom,
state->margin.left);
wlr_layer_surface_v1_configure(layer, box.width, box.height);
}
}
#include "node.h"
void
arrange_layers(struct output *output)
layers_arrange(struct output *output)
{
assert(output);
struct wlr_box usable_area = { 0 };
struct wlr_box full_area = { 0 };
wlr_output_effective_resolution(output->wlr_output,
&usable_area.width, &usable_area.height);
&full_area.width, &full_area.height);
struct wlr_box usable_area = full_area;
struct server *server = output->server;
struct wlr_scene_output *scene_output =
wlr_scene_get_scene_output(server->scene, output->wlr_output);
if (!scene_output) {
wlr_log(WLR_DEBUG, "no wlr_scene_output");
return;
}
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;
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);
}
wlr_scene_node_set_position(&output->layer_tree[i]->node,
scene_output->x, scene_output->y);
}
/* Exclusive surfaces */
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
&usable_area, true);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
&usable_area, true);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
&usable_area, true);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
&usable_area, true);
memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
/* TODO: re-arrange all views taking into account updated usable_area */
/* Non-exclusive surfaces */
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
&usable_area, false);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
&usable_area, false);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
&usable_area, false);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
&usable_area, false);
/* Find topmost keyboard interactive layer, if such a layer exists */
uint32_t layers_above_shell[] = {
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
ZWLR_LAYER_SHELL_V1_LAYER_TOP,
};
size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
size_t nlayers = sizeof(layers_above_shell)
/ sizeof(layers_above_shell[0]);
struct lab_layer_surface *layer, *topmost = NULL;
for (size_t i = 0; i < nlayers; ++i) {
wl_list_for_each_reverse (layer,
&output->layers[layers_above_shell[i]], link) {
if (layer->layer_surface->current.keyboard_interactive) {
struct wlr_layer_surface_v1 *layer_surface =
layer->scene_layer_surface->layer_surface;
if (layer_surface->current.keyboard_interactive) {
topmost = layer;
break;
}
@ -228,11 +74,13 @@ arrange_layers(struct output *output)
}
struct seat *seat = &output->server->seat;
if (topmost) {
seat_set_focus_layer(seat, topmost->layer_surface);
seat_set_focus_layer(seat,
topmost->scene_layer_surface->layer_surface);
} else if (seat->focused_layer &&
!seat->focused_layer->current.keyboard_interactive) {
seat_set_focus_layer(seat, NULL);
}
/* FIXME: should we call a desktop_arrange_all_views() here? */
}
static void
@ -240,9 +88,8 @@ output_destroy_notify(struct wl_listener *listener, void *data)
{
struct lab_layer_surface *layer =
wl_container_of(listener, layer, output_destroy);
layer->layer_surface->output = NULL;
wl_list_remove(&layer->output_destroy.link);
wlr_layer_surface_v1_destroy(layer->layer_surface);
wlr_layer_surface_v1_destroy(layer->scene_layer_surface->layer_surface);
}
static void
@ -250,8 +97,10 @@ surface_commit_notify(struct wl_listener *listener, void *data)
{
struct lab_layer_surface *layer =
wl_container_of(listener, layer, surface_commit);
struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface;
struct wlr_output *wlr_output = layer->layer_surface->output;
struct wlr_layer_surface_v1 *layer_surface =
layer->scene_layer_surface->layer_surface;
struct wlr_output *wlr_output =
layer->scene_layer_surface->layer_surface->output;
if (!wlr_output) {
return;
@ -262,19 +111,17 @@ surface_commit_notify(struct wl_listener *listener, void *data)
layer->mapped = layer_surface->mapped;
struct output *output =
output_from_wlr_output(layer->server, wlr_output);
arrange_layers(output);
layers_arrange(output);
}
damage_all_outputs(layer->server);
}
static void
unmap(struct lab_layer_surface *layer)
{
struct seat *seat = &layer->server->seat;
if (seat->focused_layer == layer->layer_surface) {
if (seat->focused_layer == layer->scene_layer_surface->layer_surface) {
seat_set_focus_layer(seat, NULL);
}
damage_all_outputs(layer->server);
}
static void
@ -282,18 +129,20 @@ destroy_notify(struct wl_listener *listener, void *data)
{
struct lab_layer_surface *layer = wl_container_of(
listener, layer, destroy);
if (layer->layer_surface->mapped) {
if (layer->scene_layer_surface->layer_surface->mapped) {
unmap(layer);
}
wl_list_remove(&layer->link);
wl_list_remove(&layer->destroy.link);
wl_list_remove(&layer->map.link);
wl_list_remove(&layer->unmap.link);
wl_list_remove(&layer->surface_commit.link);
if (layer->layer_surface->output) {
if (layer->scene_layer_surface->layer_surface->output) {
wl_list_remove(&layer->output_destroy.link);
struct output *output = output_from_wlr_output(
layer->server, layer->layer_surface->output);
arrange_layers(output);
struct output *output = output_from_wlr_output(layer->server,
layer->scene_layer_surface->layer_surface->output);
layers_arrange(output);
}
free(layer);
}
@ -301,150 +150,18 @@ destroy_notify(struct wl_listener *listener, void *data)
static void
unmap_notify(struct wl_listener *listener, void *data)
{
struct lab_layer_surface *l = wl_container_of(listener, l, unmap);
unmap(l);
return;
struct lab_layer_surface *lab_layer_surface =
wl_container_of(listener, lab_layer_surface, unmap);
unmap(lab_layer_surface);
}
static void
map_notify(struct wl_listener *listener, void *data)
{
struct wlr_layer_surface_v1 *l = data;
wlr_surface_send_enter(l->surface, l->output);
}
static void
subsurface_damage(struct lab_layer_subsurface *subsurface, bool whole)
{
struct lab_layer_surface *layer = subsurface->layer_surface;
struct wlr_output *wlr_output = layer->layer_surface->output;
if (!wlr_output) {
return;
}
// struct output *output = wlr_output->data;
// int ox = subsurface->wlr_subsurface->current.x + layer->geo.x;
// int oy = subsurface->wlr_subsurface->current.y + layer->geo.y;
damage_all_outputs(layer->server);
}
static void
subsurface_handle_unmap(struct wl_listener *listener, void *data)
{
struct lab_layer_subsurface *subsurface =
wl_container_of(listener, subsurface, unmap);
subsurface_damage(subsurface, true);
}
static void
subsurface_handle_map(struct wl_listener *listener, void *data)
{
struct lab_layer_subsurface *subsurface =
wl_container_of(listener, subsurface, map);
subsurface_damage(subsurface, true);
}
static void
subsurface_handle_commit(struct wl_listener *listener, void *data)
{
struct lab_layer_subsurface *subsurface =
wl_container_of(listener, subsurface, commit);
subsurface_damage(subsurface, false);
}
static void
subsurface_handle_destroy(struct wl_listener *listener, void *data)
{
struct lab_layer_subsurface *subsurface =
wl_container_of(listener, subsurface, destroy);
wl_list_remove(&subsurface->map.link);
wl_list_remove(&subsurface->unmap.link);
wl_list_remove(&subsurface->destroy.link);
wl_list_remove(&subsurface->commit.link);
free(subsurface);
}
static struct
lab_layer_subsurface *create_subsurface(struct wlr_subsurface *wlr_subsurface,
struct lab_layer_surface *layer_surface)
{
struct lab_layer_subsurface *subsurface =
calloc(1, sizeof(struct lab_layer_subsurface));
if (!subsurface) {
return NULL;
}
subsurface->wlr_subsurface = wlr_subsurface;
subsurface->layer_surface = layer_surface;
subsurface->map.notify = subsurface_handle_map;
wl_signal_add(&wlr_subsurface->events.map, &subsurface->map);
subsurface->unmap.notify = subsurface_handle_unmap;
wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap);
subsurface->destroy.notify = subsurface_handle_destroy;
wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
subsurface->commit.notify = subsurface_handle_commit;
wl_signal_add(&wlr_subsurface->surface->events.commit,
&subsurface->commit);
return subsurface;
}
static void
new_subsurface_notify(struct wl_listener *listener, void *data)
{
struct lab_layer_surface *lab_layer_surface =
wl_container_of(listener, lab_layer_surface, new_subsurface);
struct wlr_subsurface *wlr_subsurface = data;
create_subsurface(wlr_subsurface, lab_layer_surface);
}
static struct
lab_layer_surface *popup_get_layer(struct lab_layer_popup *popup)
{
while (popup->parent_type == LAYER_PARENT_POPUP) {
popup = popup->parent_popup;
}
return popup->parent_layer;
}
static void
popup_damage(struct lab_layer_popup *layer_popup, bool whole)
{
struct lab_layer_surface *layer;
while (true) {
if (layer_popup->parent_type == LAYER_PARENT_POPUP) {
layer_popup = layer_popup->parent_popup;
} else {
layer = layer_popup->parent_layer;
break;
}
}
damage_all_outputs(layer->server);
}
static void
popup_handle_map(struct wl_listener *listener, void *data)
{
struct lab_layer_popup *popup = wl_container_of(listener, popup, map);
struct lab_layer_surface *layer = popup_get_layer(popup);
struct wlr_output *wlr_output = layer->layer_surface->output;
wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output);
popup_damage(popup, true);
}
static void
popup_handle_unmap(struct wl_listener *listener, void *data)
{
struct lab_layer_popup *popup = wl_container_of(listener, popup, unmap);
popup_damage(popup, true);
}
static void
popup_handle_commit(struct wl_listener *listener, void *data)
{
struct lab_layer_popup *popup = wl_container_of(listener, popup, commit);
popup_damage(popup, false);
return;
struct wlr_layer_surface_v1 *layer_surface = data;
wlr_surface_send_enter(layer_surface->surface, layer_surface->output);
}
static void
@ -452,40 +169,16 @@ popup_handle_destroy(struct wl_listener *listener, void *data)
{
struct lab_layer_popup *popup =
wl_container_of(listener, popup, destroy);
wl_list_remove(&popup->map.link);
wl_list_remove(&popup->unmap.link);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->commit.link);
wl_list_remove(&popup->new_popup.link);
free(popup);
}
static void
popup_unconstrain(struct lab_layer_popup *popup)
{
struct lab_layer_surface *layer = popup_get_layer(popup);
struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
struct output *output = layer->layer_surface->output->data;
struct wlr_box output_box = { 0 };
wlr_output_effective_resolution(output->wlr_output, &output_box.width,
&output_box.height);
struct wlr_box output_toplevel_sx_box = {
.x = -layer->geo.x,
.y = -layer->geo.y,
.width = output_box.width,
.height = output_box.height,
};
wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
}
static void popup_handle_new_popup(struct wl_listener *listener, void *data);
static struct lab_layer_popup *
create_popup(struct wlr_xdg_popup *wlr_popup,
enum layer_parent parent_type, void *parent)
create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_node *parent,
struct wlr_box *output_toplevel_sx_box)
{
struct lab_layer_popup *popup =
calloc(1, sizeof(struct lab_layer_popup));
@ -494,41 +187,98 @@ create_popup(struct wlr_xdg_popup *wlr_popup,
}
popup->wlr_popup = wlr_popup;
popup->parent_type = parent_type;
popup->parent_layer = parent;
popup->scene_node =
wlr_scene_xdg_surface_create(parent, wlr_popup->base);
if (!popup->scene_node) {
free(popup);
return NULL;
}
node_descriptor_create(popup->scene_node,
LAB_NODE_DESC_LAYER_POPUP, popup);
popup->map.notify = popup_handle_map;
wl_signal_add(&wlr_popup->base->events.map, &popup->map);
popup->unmap.notify = popup_handle_unmap;
wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
popup->destroy.notify = popup_handle_destroy;
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
popup->commit.notify = popup_handle_commit;
wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
popup->new_popup.notify = popup_handle_new_popup;
wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
popup_unconstrain(popup);
wlr_xdg_popup_unconstrain_from_box(wlr_popup, output_toplevel_sx_box);
return popup;
}
/* This popup's parent is a layer popup */
static void
popup_handle_new_popup(struct wl_listener *listener, void *data)
{
struct lab_layer_popup *lab_layer_popup =
wl_container_of(listener, lab_layer_popup, new_popup);
struct wlr_xdg_popup *wlr_popup = data;
create_popup(wlr_popup, LAYER_PARENT_POPUP, lab_layer_popup);
struct lab_layer_popup *new_popup = create_popup(wlr_popup,
lab_layer_popup->scene_node,
&lab_layer_popup->output_toplevel_sx_box);
new_popup->output_toplevel_sx_box =
lab_layer_popup->output_toplevel_sx_box;
}
/*
* We move popups from the bottom to the top layer so that they are
* rendered above views.
*/
static void
move_popup_to_top_layer(struct lab_layer_surface *toplevel,
struct lab_layer_popup *popup)
{
struct server *server = toplevel->server;
struct wlr_output *wlr_output =
toplevel->scene_layer_surface->layer_surface->output;
struct output *output = output_from_wlr_output(server, wlr_output);
struct wlr_box box = { 0 };
wlr_output_layout_get_box(server->output_layout, wlr_output, &box);
int lx = toplevel->scene_layer_surface->node->state.x + box.x;
int ly = toplevel->scene_layer_surface->node->state.y + box.y;
struct wlr_scene_node *node = popup->scene_node;
wlr_scene_node_reparent(node, &output->layer_popup_tree->node);
wlr_scene_node_set_position(&output->layer_popup_tree->node, lx, ly);
}
/* This popup's parent is a shell-layer surface */
static void
new_popup_notify(struct wl_listener *listener, void *data)
{
struct lab_layer_surface *lab_layer_surface =
wl_container_of(listener, lab_layer_surface, new_popup);
struct lab_layer_surface *toplevel =
wl_container_of(listener, toplevel, new_popup);
struct wlr_xdg_popup *wlr_popup = data;
create_popup(wlr_popup, LAYER_PARENT_LAYER, lab_layer_surface);
struct output *output =
toplevel->scene_layer_surface->layer_surface->output->data;
struct wlr_box output_box = { 0 };
wlr_output_layout_get_box(output->server->output_layout,
output->wlr_output, &output_box);
int lx, ly;
wlr_scene_node_coords(toplevel->scene_layer_surface->node, &lx, &ly);
/*
* Output geometry expressed in the coordinate system of the toplevel
* parent of popup. We store this struct the lab_layer_popup struct
* to make it easier to unconstrain children when we move popups from
* the bottom to the top layer.
*/
struct wlr_box output_toplevel_sx_box = {
.x = output_box.x - lx,
.y = output_box.y - ly,
.width = output_box.width,
.height = output_box.height,
};
struct lab_layer_popup *popup = create_popup(wlr_popup,
toplevel->scene_layer_surface->node,
&output_toplevel_sx_box);
popup->output_toplevel_sx_box = output_toplevel_sx_box;
if (toplevel->scene_layer_surface->layer_surface->current.layer
== ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) {
move_popup_to_top_layer(toplevel, popup);
}
}
static void
@ -567,15 +317,19 @@ new_layer_surface_notify(struct wl_listener *listener, void *data)
surface->new_popup.notify = new_popup_notify;
wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup);
surface->new_subsurface.notify = new_subsurface_notify;
wl_signal_add(&layer_surface->surface->events.new_subsurface,
&surface->new_subsurface);
surface->layer_surface = layer_surface;
layer_surface->data = surface;
surface->server = server;
struct output *output = layer_surface->output->data;
struct wlr_scene_tree *selected_layer =
output->layer_tree[layer_surface->current.layer];
surface->scene_layer_surface = wlr_scene_layer_surface_v1_create(
&selected_layer->node, layer_surface);
node_descriptor_create(surface->scene_layer_surface->node,
LAB_NODE_DESC_LAYER_SURFACE, surface);
surface->server = server;
surface->scene_layer_surface->layer_surface = layer_surface;
surface->output_destroy.notify = output_destroy_notify;
wl_signal_add(&layer_surface->output->events.destroy,
&surface->output_destroy);
@ -593,7 +347,7 @@ new_layer_surface_notify(struct wl_listener *listener, void *data)
*/
struct wlr_layer_surface_v1_state old_state = layer_surface->current;
layer_surface->current = layer_surface->pending;
arrange_layers(output);
layers_arrange(output);
layer_surface->current = old_state;
}

View file

@ -68,7 +68,7 @@ main(int argc, char *argv[])
server_start(&server);
struct theme theme = { 0 };
theme_init(&theme, server.renderer, rc.theme_name);
theme_init(&theme, rc.theme_name);
server.theme = &theme;
menu_init_rootmenu(&server);

View file

@ -19,6 +19,8 @@
#include "menu/menu.h"
#include "theme.h"
#include "action.h"
#include "buffer.h"
#include "node.h"
#define MENUWIDTH (110)
#define MENU_ITEM_PADDING_Y (4)
@ -51,12 +53,19 @@ menu_create(struct server *server, const char *id, const char *label)
menu->label = strdup(label);
menu->parent = current_menu;
menu->server = server;
menu->size.width = MENUWIDTH;
/* menu->size.height will be kept up to date by adding items */
menu->scene_tree = wlr_scene_tree_create(&server->menu_tree->node);
wlr_scene_node_set_enabled(&menu->scene_tree->node, false);
return menu;
}
static struct menu *
get_menu_by_id(const char *id)
struct menu *
menu_get_by_id(const char *id)
{
if (!id) {
return NULL;
}
struct menu *menu;
for (int i = 0; i < nr_menus; ++i) {
menu = menus + i;
@ -74,6 +83,7 @@ item_create(struct menu *menu, const char *text)
if (!menuitem) {
return NULL;
}
menuitem->parent = menu;
struct server *server = menu->server;
struct theme *theme = server->theme;
struct font font = {
@ -81,19 +91,57 @@ item_create(struct menu *menu, const char *text)
.size = rc.font_size_menuitem,
};
menuitem->box.width = MENUWIDTH;
menuitem->box.height = font_height(&font) + 2 * MENU_ITEM_PADDING_Y;
if (!menu->item_height) {
menu->item_height = font_height(&font) + 2 * MENU_ITEM_PADDING_Y;
}
int x, y;
int item_max_width = MENUWIDTH - 2 * MENU_ITEM_PADDING_X;
font_texture_create(server, &menuitem->texture.active, item_max_width,
text, &font, theme->menu_items_active_text_color);
font_texture_create(server, &menuitem->texture.inactive, item_max_width,
text, &font, theme->menu_items_text_color);
struct wlr_scene_node *parent = &menu->scene_tree->node;
/* center align vertically */
menuitem->texture.offset_y =
(menuitem->box.height - menuitem->texture.active->height) / 2;
menuitem->texture.offset_x = MENU_ITEM_PADDING_X;
/* Font buffer */
font_buffer_create(&menuitem->normal.buffer, item_max_width,
text, &font, theme->menu_items_text_color);
font_buffer_create(&menuitem->selected.buffer, item_max_width,
text, &font, theme->menu_items_active_text_color);
/* Item background nodes */
menuitem->normal.background = &wlr_scene_rect_create(parent,
MENUWIDTH, menu->item_height,
theme->menu_items_bg_color)->node;
menuitem->selected.background = &wlr_scene_rect_create(parent,
MENUWIDTH, menu->item_height,
theme->menu_items_active_bg_color)->node;
/* Font nodes */
menuitem->normal.text = &wlr_scene_buffer_create(
menuitem->normal.background, &menuitem->normal.buffer->base)->node;
menuitem->selected.text = &wlr_scene_buffer_create(
menuitem->selected.background, &menuitem->selected.buffer->base)->node;
/* Node descriptors for top scene nodes of menuitem */
node_descriptor_create(menuitem->normal.background,
LAB_NODE_DESC_MENUITEM, menuitem);
node_descriptor_create(menuitem->selected.background,
LAB_NODE_DESC_MENUITEM, menuitem);
/* Center font nodes */
y = (menu->item_height - menuitem->normal.buffer->base.height) / 2;
x = MENU_ITEM_PADDING_X;
wlr_scene_node_set_position(menuitem->normal.text, x, y);
wlr_scene_node_set_position(menuitem->selected.text, x, y);
/* Position the item in relation to its menu */
int item_count = wl_list_length(&menu->menuitems);
y = item_count * menu->item_height;
wlr_scene_node_set_position(menuitem->normal.background, 0, y);
wlr_scene_node_set_position(menuitem->selected.background, 0, y);
/* Hide selected state */
wlr_scene_node_set_enabled(menuitem->selected.background, false);
/* Update menu extents */
menu->size.height = (item_count + 1) * menu->item_height;
wl_list_insert(&menu->menuitems, &menuitem->link);
wl_list_init(&menuitem->actions);
@ -204,7 +252,7 @@ handle_menu_element(xmlNode *n, struct server *server)
current_menu = current_menu->parent;
--menu_level;
} else if (id) {
struct menu *menu = get_menu_by_id(id);
struct menu *menu = menu_get_by_id(id);
if (menu) {
current_item = item_create(current_menu, menu->label);
current_item->submenu = menu;
@ -280,78 +328,127 @@ err:
free(b.buf);
}
static int
menu_get_full_width(struct menu *menu)
{
int width = menu->size.width - menu->server->theme->menu_overlap_x;
int child_width;
int max_child_width = 0;
struct menuitem *item;
wl_list_for_each_reverse(item, &menu->menuitems, link) {
if (!item->submenu) {
continue;
}
child_width = menu_get_full_width(item->submenu);
if (child_width > max_child_width) {
max_child_width = child_width;
}
}
return width + max_child_width;
}
static void
menu_configure(struct menu *menu, int x, int y)
menu_configure(struct menu *menu, int lx, int ly, enum menu_align align)
{
struct theme *theme = menu->server->theme;
menu->box.x = x;
menu->box.y = y;
/* Get output local coordinates + output usable area */
double ox = lx;
double oy = ly;
struct wlr_output *wlr_output = wlr_output_layout_output_at(
menu->server->output_layout, lx, ly);
wlr_output_layout_output_coords(menu->server->output_layout,
wlr_output, &ox, &oy);
struct wlr_box usable = output_usable_area_from_cursor_coords(menu->server);
int offset = 0;
struct menuitem *menuitem;
wl_list_for_each_reverse (menuitem, &menu->menuitems, link) {
menuitem->box.x = menu->box.x;
menuitem->box.y = menu->box.y + offset;
offset += menuitem->box.height;
if (menuitem->submenu) {
menu_configure(menuitem->submenu, menuitem->box.x
+ MENUWIDTH - theme->menu_overlap_x,
menuitem->box.y + theme->menu_overlap_y);
if (align == LAB_MENU_OPEN_AUTO) {
int full_width = menu_get_full_width(menu);
if (ox + full_width > usable.width) {
align = LAB_MENU_OPEN_LEFT;
} else {
align = LAB_MENU_OPEN_RIGHT;
}
}
menu->box.width = MENUWIDTH;
menu->box.height = offset;
if (oy + menu->size.height > usable.height) {
align &= ~LAB_MENU_OPEN_BOTTOM;
align |= LAB_MENU_OPEN_TOP;
} else {
align &= ~LAB_MENU_OPEN_TOP;
align |= LAB_MENU_OPEN_BOTTOM;
}
if (align & LAB_MENU_OPEN_LEFT) {
lx -= MENUWIDTH - theme->menu_overlap_x;
}
if (align & LAB_MENU_OPEN_TOP) {
ly -= menu->size.height;
if (menu->parent) {
/* For submenus adjust y to bottom left corner */
ly += menu->item_height;
}
}
wlr_scene_node_set_position(&menu->scene_tree->node, lx, ly);
int rel_y;
int new_lx, new_ly;
struct menuitem *item;
wl_list_for_each_reverse(item, &menu->menuitems, link) {
if (!item->submenu) {
continue;
}
if (align & LAB_MENU_OPEN_RIGHT) {
new_lx = lx + MENUWIDTH - theme->menu_overlap_x;
} else {
new_lx = lx;
}
rel_y = item->normal.background->state.y;
new_ly = ly + rel_y - theme->menu_overlap_y;
menu_configure(item->submenu, new_lx, new_ly, align);
}
}
void
menu_init_rootmenu(struct server *server)
{
parse_xml("menu.xml", server);
server->rootmenu = get_menu_by_id("root-menu");
struct menu *menu = menu_get_by_id("root-menu");
/* Default menu if no menu.xml found */
if (!server->rootmenu) {
if (!menu) {
current_menu = NULL;
server->rootmenu = menu_create(server, "root-menu", "");
menu = menu_create(server, "root-menu", "");
}
if (wl_list_empty(&server->rootmenu->menuitems)) {
current_item = item_create(server->rootmenu, "Reconfigure");
if (wl_list_empty(&menu->menuitems)) {
current_item = item_create(menu, "Reconfigure");
fill_item("name.action", "Reconfigure");
current_item = item_create(server->rootmenu, "Exit");
current_item = item_create(menu, "Exit");
fill_item("name.action", "Exit");
}
server->rootmenu->visible = true;
menu_configure(server->rootmenu, 100, 100);
}
void
menu_init_windowmenu(struct server *server)
{
server->windowmenu = get_menu_by_id("client-menu");
struct menu *menu = menu_get_by_id("client-menu");
/* Default menu if no menu.xml found */
if (!server->windowmenu) {
if (!menu) {
current_menu = NULL;
server->windowmenu = menu_create(server, "client-menu", "");
menu = menu_create(server, "client-menu", "");
}
if (wl_list_empty(&server->windowmenu->menuitems)) {
current_item = item_create(server->windowmenu, "Minimize");
if (wl_list_empty(&menu->menuitems)) {
current_item = item_create(menu, "Minimize");
fill_item("name.action", "Iconify");
current_item = item_create(server->windowmenu, "Maximize");
current_item = item_create(menu, "Maximize");
fill_item("name.action", "ToggleMaximize");
current_item = item_create(server->windowmenu, "Fullscreen");
current_item = item_create(menu, "Fullscreen");
fill_item("name.action", "ToggleFullscreen");
current_item = item_create(server->windowmenu, "Decorations");
current_item = item_create(menu, "Decorations");
fill_item("name.action", "ToggleDecorations");
current_item = item_create(server->windowmenu, "Close");
current_item = item_create(menu, "Close");
fill_item("name.action", "Close");
}
server->windowmenu->visible = true;
menu_configure(server->windowmenu, 100, 100);
}
void
@ -364,104 +461,130 @@ menu_finish(void)
wl_list_for_each_safe(item, next, &menu->menuitems, link) {
wl_list_remove(&item->link);
action_list_free(&item->actions);
wlr_scene_node_destroy(item->normal.text);
wlr_scene_node_destroy(item->selected.text);
wlr_scene_node_destroy(item->normal.background);
wlr_scene_node_destroy(item->selected.background);
wlr_buffer_drop(&item->normal.buffer->base);
wlr_buffer_drop(&item->selected.buffer->base);
free(item);
}
wlr_scene_node_destroy(&menu->scene_tree->node);
}
zfree(menus);
alloc_menus = 0;
nr_menus = 0;
}
/* Sets selection (or clears selection if passing NULL) */
static void
menu_set_selection(struct menu *menu, struct menuitem *item)
{
/* Clear old selection */
if (menu->selection.item) {
wlr_scene_node_set_enabled(
menu->selection.item->normal.background, true);
wlr_scene_node_set_enabled(
menu->selection.item->selected.background, false);
}
/* Set new selection */
if (item) {
wlr_scene_node_set_enabled(item->normal.background, false);
wlr_scene_node_set_enabled(item->selected.background, true);
}
menu->selection.item = item;
}
static void
close_all_submenus(struct menu *menu)
{
struct menuitem *item;
wl_list_for_each (item, &menu->menuitems, link) {
if (item->submenu) {
item->submenu->visible = false;
wlr_scene_node_set_enabled(&item->submenu->scene_tree->node, false);
close_all_submenus(item->submenu);
}
}
menu->selection.menu = NULL;
}
void
menu_move(struct menu *menu, int x, int y)
menu_open(struct menu *menu, int x, int y)
{
assert(menu);
if (menu->server->menu_current) {
menu_close(menu->server->menu_current);
}
close_all_submenus(menu);
menu_configure(menu, x, y);
menu_set_selection(menu, NULL);
menu_configure(menu, x, y, LAB_MENU_OPEN_AUTO);
wlr_scene_node_set_enabled(&menu->scene_tree->node, true);
menu->server->menu_current = menu;
menu->server->input_mode = LAB_INPUT_STATE_MENU;
}
/* TODO: consider renaming function to menu_process_cursor_motion */
void
menu_set_selected(struct menu *menu, int x, int y)
menu_process_cursor_motion(struct wlr_scene_node *node)
{
if (!menu->visible) {
assert(node && node->data);
struct menuitem *item = node_menuitem_from_node(node);
if (node == item->selected.background) {
/* We are on an already selected item */
return;
}
struct menuitem *item;
wl_list_for_each (item, &menu->menuitems, link) {
item->selected = wlr_box_contains_point(&item->box, x, y);
if (!item->selected) {
if (item->submenu && item->submenu->visible) {
/*
* Handle the case where a submenu is already
* open.
*/
item->selected = true;
menu_set_selected(item->submenu, x, y);
}
continue;
}
/* We're now on an item that has mouse-focus */
if (item->submenu) {
if (item->submenu->visible) {
/* do nothing - submenu already open */
} else {
/* open submenu */
close_all_submenus(menu);
item->submenu->visible = true;
menu_set_selected(item->submenu, x, y);
}
} else {
close_all_submenus(menu);
}
/* We are on an item that has new mouse-focus */
menu_set_selection(item->parent, item);
if (item->parent->selection.menu) {
/* Close old submenu tree */
menu_close(item->parent->selection.menu);
}
if (item->submenu) {
/* And open the new one */
wlr_scene_node_set_enabled(
&item->submenu->scene_tree->node, true);
}
item->parent->selection.menu = item->submenu;
}
static void
menu_clear_selection(struct menu *menu)
bool
menu_call_actions(struct wlr_scene_node *node)
{
struct menuitem *item;
wl_list_for_each (item, &menu->menuitems, link) {
item->selected = false;
if (item->submenu) {
menu_clear_selection(item->submenu);
}
assert(node && node->data);
struct menuitem *item = node_menuitem_from_node(node);
if (item->submenu) {
/* We received a click on an item that just opens a submenu */
return false;
}
actions_run(NULL, item->parent->server, &item->actions, 0);
menu_close(item->parent->server->menu_current);
item->parent->server->menu_current = NULL;
return true;
}
void
menu_close(struct menu *menu)
{
if (!menu) {
wlr_log(WLR_ERROR, "Trying to close non exiting menu");
return;
}
/* TODO: Maybe reset input state here instead of in cursor.c ? */
wlr_scene_node_set_enabled(&menu->scene_tree->node, false);
menu_set_selection(menu, NULL);
if (menu->selection.menu) {
menu_close(menu->selection.menu);
menu->selection.menu = NULL;
}
}
void
menu_action_selected(struct server *server, struct menu *menu)
{
struct menuitem *menuitem;
wl_list_for_each (menuitem, &menu->menuitems, link) {
if (menuitem->selected && !menuitem->submenu) {
action(NULL, server, &menuitem->actions, 0);
break;
}
if (menuitem->submenu) {
menu_action_selected(server, menuitem->submenu);
}
}
menu_clear_selection(menu);
}
void
menu_reconfigure(struct server *server, struct menu *menu)
menu_reconfigure(struct server *server)
{
menu_finish();
menu_init_rootmenu(server);

View file

@ -1,7 +1,8 @@
labwc_sources = files(
'action.c',
'buffer.c',
'cursor.c',
'damage.c',
'debug.c',
'desktop.c',
'foreign.c',
'interactive.c',
@ -9,16 +10,14 @@ labwc_sources = files(
'key-state.c',
'layers.c',
'main.c',
'node.c',
'osd.c',
'output.c',
'resistance.c',
'seat.c',
'server.c',
'ssd.c',
'subsurface.c',
'theme.c',
'view.c',
'view-child.c',
'view-impl.c',
'xdg.c',
'xdg-deco.c',
@ -37,3 +36,4 @@ subdir('common')
subdir('config')
subdir('xbm')
subdir('menu')
subdir('ssd')

75
src/node.c Normal file
View file

@ -0,0 +1,75 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include <stdlib.h>
#include "node.h"
static void
descriptor_destroy(struct node_descriptor *node_descriptor)
{
if (!node_descriptor) {
return;
}
wl_list_remove(&node_descriptor->destroy.link);
free(node_descriptor);
}
static void
destroy_notify(struct wl_listener *listener, void *data)
{
struct node_descriptor *node_descriptor =
wl_container_of(listener, node_descriptor, destroy);
descriptor_destroy(node_descriptor);
}
void
node_descriptor_create(struct wlr_scene_node *scene_node,
enum node_descriptor_type type, void *data)
{
struct node_descriptor *node_descriptor =
calloc(1, sizeof(struct node_descriptor));
if (!node_descriptor) {
return;
}
node_descriptor->type = type;
node_descriptor->data = data;
node_descriptor->destroy.notify = destroy_notify;
wl_signal_add(&scene_node->events.destroy, &node_descriptor->destroy);
scene_node->data = node_descriptor;
}
struct view *
node_view_from_node(struct wlr_scene_node *wlr_scene_node)
{
assert(wlr_scene_node->data);
struct node_descriptor *node_descriptor = wlr_scene_node->data;
assert(node_descriptor->type == LAB_NODE_DESC_VIEW
|| node_descriptor->type == LAB_NODE_DESC_XDG_POPUP);
return (struct view *)node_descriptor->data;
}
struct lab_layer_surface *
node_layer_surface_from_node(struct wlr_scene_node *wlr_scene_node)
{
assert(wlr_scene_node->data);
struct node_descriptor *node_descriptor = wlr_scene_node->data;
assert(node_descriptor->type == LAB_NODE_DESC_LAYER_SURFACE);
return (struct lab_layer_surface *)node_descriptor->data;
}
struct lab_layer_popup *
node_layer_popup_from_node(struct wlr_scene_node *wlr_scene_node)
{
assert(wlr_scene_node->data);
struct node_descriptor *node_descriptor = wlr_scene_node->data;
assert(node_descriptor->type == LAB_NODE_DESC_LAYER_POPUP);
return (struct lab_layer_popup *)node_descriptor->data;
}
struct menuitem *
node_menuitem_from_node(struct wlr_scene_node *wlr_scene_node)
{
assert(wlr_scene_node->data);
struct node_descriptor *node_descriptor = wlr_scene_node->data;
assert(node_descriptor->type == LAB_NODE_DESC_MENUITEM);
return (struct menuitem *)node_descriptor->data;
}

View file

@ -4,6 +4,7 @@
#include <drm_fourcc.h>
#include <pango/pangocairo.h>
#include <wlr/util/log.h>
#include "buffer.h"
#include "common/buf.h"
#include "common/font.h"
#include "config/rcxml.h"
@ -73,22 +74,42 @@ get_osd_height(struct wl_list *views)
return height;
}
static void
destroy_osd_nodes(struct server *server)
{
struct wlr_scene_node *child, *next;
struct wl_list *children = &server->osd_tree->node.state.children;
wl_list_for_each_safe(child, next, children, state.link) {
wlr_scene_node_destroy(child);
}
}
void
osd_finish(struct server *server)
{
destroy_osd_nodes(server);
wlr_scene_node_set_enabled(&server->osd_tree->node, false);
}
void
osd_update(struct server *server)
{
struct wlr_renderer *renderer = server->renderer;
struct theme *theme = server->theme;
destroy_osd_nodes(server);
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
float scale = output->wlr_output->scale;
int w = (OSD_ITEM_WIDTH + (2 * OSD_BORDER_WIDTH)) * scale;
int h = get_osd_height(&server->views) * scale;
cairo_surface_t *surf =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
cairo_surface_set_device_scale(surf, scale, scale);
cairo_t *cairo = cairo_create(surf);
if (output->osd_buffer) {
wlr_buffer_drop(&output->osd_buffer->base);
}
output->osd_buffer = buffer_create_cairo(w, h, scale, true);
cairo_t *cairo = output->osd_buffer->cairo;
cairo_surface_t *surf = cairo_get_target(cairo);
/* background */
set_source(cairo, theme->osd_bg_color);
@ -176,21 +197,14 @@ osd_update(struct server *server)
pango_cairo_show_layout(cairo, layout);
y += OSD_ITEM_HEIGHT;
}
g_object_unref(layout);
/* convert to wlr_texture */
cairo_surface_flush(surf);
unsigned char *data = cairo_image_surface_get_data(surf);
struct wlr_texture *texture = wlr_texture_from_pixels(renderer,
DRM_FORMAT_ARGB8888, cairo_image_surface_get_stride(surf),
w, h, data);
cairo_destroy(cairo);
cairo_surface_destroy(surf);
if (output->osd) {
wlr_texture_destroy(output->osd);
}
output->osd = texture;
struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create(
&server->osd_tree->node, &output->osd_buffer->base);
/* TODO: set position properly */
wlr_scene_node_set_position(&scene_buffer->node, 10, 10);
wlr_scene_node_set_enabled(&server->osd_tree->node, true);
}
}

File diff suppressed because it is too large Load diff

View file

@ -310,7 +310,6 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer)
if (!layer) {
seat->focused_layer = NULL;
desktop_focus_topmost_mapped_view(seat->server);
damage_all_outputs(seat->server);
return;
}
seat_focus_surface(seat, layer->surface);

View file

@ -7,6 +7,7 @@
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_input_inhibitor.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_primary_selection_v1.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_viewporter.h>
@ -32,7 +33,7 @@ reload_config_and_theme(void)
rcxml_finish();
rcxml_read(NULL);
theme_finish(g_server->theme);
theme_init(g_server->theme, g_server->renderer, rc.theme_name);
theme_init(g_server->theme, rc.theme_name);
struct view *view;
wl_list_for_each (view, &g_server->views, link) {
@ -40,12 +41,11 @@ reload_config_and_theme(void)
continue;
}
view->margin = ssd_thickness(view);
ssd_update_geometry(view, true);
ssd_reload(view);
}
menu_reconfigure(g_server, g_server->rootmenu);
menu_reconfigure(g_server);
seat_reconfigure(g_server);
damage_all_outputs(g_server);
}
static int
@ -120,7 +120,7 @@ seat_disinhibit_input(struct seat *seat)
*/
struct output *output;
wl_list_for_each(output, &seat->server->outputs, link) {
arrange_layers(output);
layers_arrange(output);
}
}
@ -216,6 +216,20 @@ server_init(struct server *server)
wl_list_init(&server->views);
wl_list_init(&server->unmanaged_surfaces);
server->scene = wlr_scene_create();
if (!server->scene) {
wlr_log(WLR_ERROR, "unable to create scene");
exit(EXIT_FAILURE);
}
server->view_tree = wlr_scene_tree_create(&server->scene->node);
#if HAVE_XWAYLAND
server->unmanaged_tree = wlr_scene_tree_create(&server->scene->node);
#endif
server->menu_tree = wlr_scene_tree_create(&server->scene->node);
server->osd_tree = wlr_scene_tree_create(&server->scene->node);
output_init(server);
/*
* Create some hands-off wlroots interfaces. The compositor is
* necessary for clients to allocate surfaces and the data device
@ -229,6 +243,7 @@ server_init(struct server *server)
wlr_log(WLR_ERROR, "unable to create the wlroots compositor");
exit(EXIT_FAILURE);
}
wlr_subcompositor_create(server->wl_display);
struct wlr_data_device_manager *device_manager = NULL;
device_manager = wlr_data_device_manager_create(server->wl_display);
@ -249,7 +264,6 @@ server_init(struct server *server)
*/
wlr_primary_selection_v1_device_manager_create(server->wl_display);
output_init(server);
seat_init(server);
/* Init xdg-shell */
@ -284,6 +298,14 @@ server_init(struct server *server)
WLR_SERVER_DECORATION_MANAGER_MODE_SERVER :
WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
struct wlr_presentation *presentation =
wlr_presentation_create(server->wl_display, server->backend);
if (!presentation) {
wlr_log(WLR_ERROR, "unable to create presentation interface");
exit(EXIT_FAILURE);
}
wlr_scene_set_presentation(server->scene, presentation);
wlr_export_dmabuf_manager_v1_create(server->wl_display);
wlr_screencopy_manager_v1_create(server->wl_display);
wlr_data_control_manager_v1_create(server->wl_display);
@ -394,6 +416,8 @@ server_start(struct server *server)
void
server_finish(struct server *server)
{
/* TODO: clean up various scene_tree nodes */
#if HAVE_XWAYLAND
wlr_xwayland_destroy(server->xwayland);
#endif

466
src/ssd.c
View file

@ -1,466 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Helpers for view server side decorations
*
* Copyright (C) Johan Malm 2020-2021
*/
#include <assert.h>
#include "config/rcxml.h"
#include "common/font.h"
#include "labwc.h"
#include "theme.h"
#include "ssd.h"
#define INVISIBLE_MARGIN (16)
struct border
ssd_thickness(struct view *view)
{
struct theme *theme = view->server->theme;
struct border border = {
.top = theme->title_height + theme->border_width,
.bottom = theme->border_width,
.left = theme->border_width,
.right = theme->border_width,
};
return border;
}
struct wlr_box
ssd_max_extents(struct view *view)
{
struct border border = ssd_thickness(view);
struct wlr_box box = {
.x = view->x - border.left,
.y = view->y - border.top,
.width = view->w + border.left + border.right,
.height = view->h + border.top + border.bottom,
};
return box;
}
#define NR_BUTTONS (4)
/**
* ssd_box - the 'full' decoration geometry which includes both visible
* and invisible parts. It typically includes an invisible margin outside
* the decoration.
*
* This function is used for determining decoration parts during user-
* interactive operations such as mouse hover or button press
*/
static struct wlr_box
ssd_box(struct view *view, enum ssd_part_type type)
{
struct theme *theme = view->server->theme;
struct wlr_box box = { 0 };
int16_t button_height = theme->title_height;
int16_t button_width = theme->title_height;
int16_t corner_square = theme->title_height + theme->border_width;
int16_t title_x_padding = (double)corner_square / 2;
switch (type) {
case LAB_SSD_BUTTON_CLOSE:
box.x = view->x + view->w - button_width * 1;
box.y = view->y - button_height;
box.width = button_width;
box.height = button_height;
break;
case LAB_SSD_BUTTON_MAXIMIZE:
box.x = view->x + view->w - button_width * 2;
box.y = view->y - button_height;
box.width = button_width;
box.height = button_height;
break;
case LAB_SSD_BUTTON_ICONIFY:
box.x = view->x + view->w - button_width * 3;
box.y = view->y - button_height;
box.width = button_width;
box.height = button_height;
break;
case LAB_SSD_BUTTON_WINDOW_MENU:
box.x = view->x;
box.y = view->y - button_height;
box.width = button_width;
box.height = button_height;
break;
case LAB_SSD_PART_TITLEBAR:
box.x = view->x;
box.y = view->y - theme->title_height;
box.width = view->w;
box.height = theme->title_height;
break;
case LAB_SSD_PART_TITLE:
box.x = view->x + button_width + title_x_padding;
box.y = view->y - theme->title_height;
box.width = view->w - title_x_padding * 2 - NR_BUTTONS * button_width;
box.height = theme->title_height;
break;
case LAB_SSD_PART_CORNER_TOP_LEFT:
box.x = view->x - theme->border_width - INVISIBLE_MARGIN;
box.y = view->y - corner_square - INVISIBLE_MARGIN;
box.width = corner_square + INVISIBLE_MARGIN;
box.height = corner_square + INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_CORNER_TOP_RIGHT:
box.x = view->x + view->w - theme->title_height;
box.y = view->y - corner_square - INVISIBLE_MARGIN;
box.width = corner_square + INVISIBLE_MARGIN;
box.height = corner_square + INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_CORNER_BOTTOM_RIGHT:
box.x = view->x + view->w - corner_square;
box.y = view->y + view->h - corner_square;
box.width = corner_square + INVISIBLE_MARGIN;
box.height = corner_square + INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_CORNER_BOTTOM_LEFT:
box.x = view->x - theme->border_width - INVISIBLE_MARGIN;
box.y = view->y + view->h - corner_square;
box.width = corner_square + INVISIBLE_MARGIN;
box.height = corner_square + INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_TOP:
box.x = view->x + theme->title_height;
box.y = view->y - corner_square - INVISIBLE_MARGIN;
box.width = view->w - 2 * theme->title_height;
box.height = theme->border_width + INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_RIGHT:
box.x = view->x + view->w;
box.y = view->y;
box.width = theme->border_width + INVISIBLE_MARGIN;
box.height = view->h;
break;
case LAB_SSD_PART_BOTTOM:
box.x = view->x - theme->border_width;
box.y = view->y + view->h;
box.width = view->w + 2 * theme->border_width;
box.height = theme->border_width + INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_LEFT:
box.x = view->x - theme->border_width - INVISIBLE_MARGIN;
box.y = view->y;
box.width = theme->border_width + INVISIBLE_MARGIN;
box.height = view->h;
break;
case LAB_SSD_CLIENT:
box.x = view->x;
box.y = view->y;
box.width = view->w;
box.height = view->h;
break;
default:
break;
}
return box;
}
static void
center_vertically(struct wlr_box *box, struct wlr_texture *texture)
{
if (!texture) {
return;
}
box->y += (box->height - texture->height) / 2;
}
static void
center_horizontally(struct view *view, struct wlr_box *box,
struct wlr_texture *texture)
{
if (!texture) {
return;
}
box->x = view->x + (view->w - texture->width) / 2;
}
static void
justify_right(struct view *view, struct wlr_box *box,
struct wlr_texture *texture)
{
if (!texture) {
return;
}
box->x = view->x + (box->width - texture->width);
}
bool
ssd_is_button(enum ssd_part_type type)
{
return type == LAB_SSD_BUTTON_CLOSE ||
type == LAB_SSD_BUTTON_MAXIMIZE ||
type == LAB_SSD_BUTTON_ICONIFY ||
type == LAB_SSD_BUTTON_WINDOW_MENU;
}
struct wlr_box
ssd_visible_box(struct view *view, enum ssd_part_type type)
{
struct theme *theme = view->server->theme;
struct wlr_box box = { 0 };
switch (type) {
case LAB_SSD_BUTTON_CLOSE:
box = ssd_box(view, type);
break;
case LAB_SSD_BUTTON_MAXIMIZE:
box = ssd_box(view, type);
break;
case LAB_SSD_BUTTON_ICONIFY:
box = ssd_box(view, type);
break;
case LAB_SSD_BUTTON_WINDOW_MENU:
box = ssd_box(view, type);
break;
case LAB_SSD_PART_TITLEBAR:
box = ssd_box(view, type);
box.x += theme->title_height;
box.width -= 2 * theme->title_height;
break;
case LAB_SSD_PART_TITLE:
box = ssd_box(view, type);
center_vertically(&box, view->title.active);
if (theme->window_label_text_justify == LAB_JUSTIFY_CENTER) {
center_horizontally(view, &box, view->title.active);
} else if (theme->window_label_text_justify == LAB_JUSTIFY_RIGHT) {
justify_right(view, &box, view->title.active);
}
if (view->title.active) {
box.width = view->title.active->width;
box.height = view->title.active->height;
}
break;
case LAB_SSD_PART_CORNER_TOP_LEFT:
box = ssd_box(view, type);
box.x += INVISIBLE_MARGIN;
box.y += INVISIBLE_MARGIN;
box.width -= INVISIBLE_MARGIN;
box.height -= INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_CORNER_TOP_RIGHT:
box = ssd_box(view, type);
box.y += INVISIBLE_MARGIN;
box.width -= INVISIBLE_MARGIN;
box.height -= INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_TOP:
box = ssd_box(view, type);
box.y += INVISIBLE_MARGIN;
box.height -= INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_RIGHT:
box = ssd_box(view, type);
box.width -= INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_BOTTOM:
box = ssd_box(view, type);
box.height -= INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_LEFT:
box = ssd_box(view, type);
box.x += INVISIBLE_MARGIN;
box.width -= INVISIBLE_MARGIN;
break;
case LAB_SSD_PART_CORNER_BOTTOM_RIGHT:
case LAB_SSD_PART_CORNER_BOTTOM_LEFT:
default:
break;
}
return box;
}
enum ssd_part_type
ssd_at(struct view *view, double lx, double ly)
{
enum ssd_part_type type;
for (type = 0; type < LAB_SSD_END_MARKER; ++type) {
struct wlr_box box = ssd_box(view, type);
if (wlr_box_contains_point(&box, lx, ly)) {
return type;
}
}
return LAB_SSD_NONE;
}
uint32_t
ssd_resize_edges(enum ssd_part_type type)
{
switch (type) {
case LAB_SSD_PART_TOP:
return WLR_EDGE_TOP;
case LAB_SSD_PART_RIGHT:
return WLR_EDGE_RIGHT;
case LAB_SSD_PART_BOTTOM:
return WLR_EDGE_BOTTOM;
case LAB_SSD_PART_LEFT:
return WLR_EDGE_LEFT;
case LAB_SSD_PART_CORNER_TOP_LEFT:
return WLR_EDGE_TOP | WLR_EDGE_LEFT;
case LAB_SSD_PART_CORNER_TOP_RIGHT:
return WLR_EDGE_RIGHT | WLR_EDGE_TOP;
case LAB_SSD_PART_CORNER_BOTTOM_RIGHT:
return WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT;
case LAB_SSD_PART_CORNER_BOTTOM_LEFT:
return WLR_EDGE_BOTTOM | WLR_EDGE_LEFT;
default:
break;
}
return 0;
}
static struct ssd_part *
add_part(struct view *view, enum ssd_part_type type)
{
struct ssd_part *part = calloc(1, sizeof(struct ssd_part));
part->type = type;
wl_list_insert(&view->ssd.parts, &part->link);
return part;
}
void
ssd_update_title(struct view *view)
{
struct theme *theme = view->server->theme;
struct font font = {
.name = rc.font_name_activewindow,
.size = rc.font_size_activewindow,
};
struct ssd_part *part;
wl_list_for_each(part, &view->ssd.parts, link) {
if (part->type == LAB_SSD_PART_TITLE) {
part->box = ssd_box(view, part->type);
break;
}
}
if (part->type != LAB_SSD_PART_TITLE) {
return;
}
int max_width = part->box.width > 0 ? part->box.width : 1000;
font_texture_create(view->server, &view->title.active, max_width,
view_get_string_prop(view, "title"),
&font, theme->window_active_label_text_color);
font_texture_create(view->server, &view->title.inactive, max_width,
view_get_string_prop(view, "title"),
&font, theme->window_inactive_label_text_color);
part->box = ssd_visible_box(view, part->type);
}
void
ssd_create(struct view *view)
{
struct theme *theme = view->server->theme;
struct ssd_part *part;
view->ssd.box.x = view->x;
view->ssd.box.y = view->y;
view->ssd.box.width = view->w;
view->ssd.box.height = view->h;
/* border */
enum ssd_part_type border[4] = {
LAB_SSD_PART_TOP,
LAB_SSD_PART_RIGHT,
LAB_SSD_PART_BOTTOM,
LAB_SSD_PART_LEFT,
};
for (int i = 0; i < 4; i++) {
part = add_part(view, border[i]);
part->box = ssd_visible_box(view, border[i]);
part->color.active = theme->window_active_border_color;
part->color.inactive = theme->window_inactive_border_color;
}
/* titlebar */
part = add_part(view, LAB_SSD_PART_TITLEBAR);
part->box = ssd_visible_box(view, LAB_SSD_PART_TITLEBAR);
part->color.active = theme->window_active_title_bg_color;
part->color.inactive = theme->window_inactive_title_bg_color;
/* titlebar top-left corner */
part = add_part(view, LAB_SSD_PART_CORNER_TOP_LEFT);
part->box = ssd_visible_box(view, part->type);
part->texture.active = &theme->corner_top_left_active_normal;
part->texture.inactive = &theme->corner_top_left_inactive_normal;
/* titlebar top-right corner */
part = add_part(view, LAB_SSD_PART_CORNER_TOP_RIGHT);
part->box = ssd_visible_box(view, part->type);
part->texture.active = &theme->corner_top_right_active_normal;
part->texture.inactive = &theme->corner_top_right_inactive_normal;
/* title text */
part = add_part(view, LAB_SSD_PART_TITLE);
ssd_update_title(view);
part->texture.active = &view->title.active;
part->texture.inactive = &view->title.inactive;
}
void
ssd_destroy(struct view *view)
{
struct ssd_part *part, *next;
wl_list_for_each_safe(part, next, &view->ssd.parts, link) {
wl_list_remove(&part->link);
free(part);
}
}
static bool
geometry_changed(struct view *view)
{
return view->x != view->ssd.box.x || view->y != view->ssd.box.y ||
view->w != view->ssd.box.width ||
view->h != view->ssd.box.height;
}
void
ssd_update_geometry(struct view *view, bool force)
{
if (!geometry_changed(view) && !force) {
return;
}
struct ssd_part *part;
wl_list_for_each(part, &view->ssd.parts, link) {
part->box = ssd_visible_box(view, part->type);
}
view->ssd.box.x = view->x;
view->ssd.box.y = view->y;
view->ssd.box.width = view->w;
view->ssd.box.height = view->h;
damage_all_outputs(view->server);
}
bool
ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate)
{
if (whole == candidate) {
return true;
}
if (whole == LAB_SSD_PART_TITLEBAR) {
return candidate >= LAB_SSD_BUTTON_CLOSE && candidate <= LAB_SSD_PART_TITLE;
}
if (whole == LAB_SSD_FRAME) {
return candidate >= LAB_SSD_BUTTON_CLOSE && candidate <= LAB_SSD_CLIENT;
}
if (whole == LAB_SSD_PART_TOP) {
return candidate == LAB_SSD_PART_CORNER_TOP_LEFT || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT;
}
if (whole == LAB_SSD_PART_RIGHT) {
return candidate == LAB_SSD_PART_CORNER_TOP_RIGHT || candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT;
}
if (whole == LAB_SSD_PART_BOTTOM) {
return candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT;
}
if (whole == LAB_SSD_PART_LEFT) {
return candidate == LAB_SSD_PART_CORNER_TOP_LEFT || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT;
}
return false;
}

7
src/ssd/meson.build Normal file
View file

@ -0,0 +1,7 @@
labwc_sources += files(
'ssd.c',
'ssd_part.c',
'ssd_titlebar.c',
'ssd_border.c',
'ssd_extents.c',
)

296
src/ssd/ssd.c Normal file
View file

@ -0,0 +1,296 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Helpers for view server side decorations
*
* Copyright (C) Johan Malm 2020-2021
*/
#include <assert.h>
#include "config/rcxml.h"
#include "common/font.h"
#include "labwc.h"
#include "theme.h"
#include "ssd.h"
/* TODO: use theme->title_height instead of SSD_HEIGHT */
struct border
ssd_thickness(struct view *view)
{
struct theme *theme = view->server->theme;
struct border border = {
.top = SSD_HEIGHT,
.bottom = theme->border_width,
.left = theme->border_width,
.right = theme->border_width,
};
return border;
}
struct wlr_box
ssd_max_extents(struct view *view)
{
struct border border = ssd_thickness(view);
struct wlr_box box = {
.x = view->x - border.left,
.y = view->y - border.top,
.width = view->w + border.left + border.right,
.height = view->h + border.top + border.bottom,
};
return box;
}
bool
ssd_is_button(enum ssd_part_type type)
{
return type == LAB_SSD_BUTTON_CLOSE
|| type == LAB_SSD_BUTTON_MAXIMIZE
|| type == LAB_SSD_BUTTON_ICONIFY
|| type == LAB_SSD_BUTTON_WINDOW_MENU;
}
enum ssd_part_type
ssd_get_part_type(struct view *view, struct wlr_scene_node *node)
{
if (!node) {
return LAB_SSD_NONE;
} else if (node->type == WLR_SCENE_NODE_SURFACE) {
return LAB_SSD_CLIENT;
} else if (!view->ssd.tree) {
return LAB_SSD_NONE;
}
struct wl_list *part_list = NULL;
struct wlr_scene_node *grandparent =
node->parent ? node->parent->parent : NULL;
/* active titlebar */
if (node->parent == &view->ssd.titlebar.active.tree->node) {
part_list = &view->ssd.titlebar.active.parts;
} else if (grandparent == &view->ssd.titlebar.active.tree->node) {
part_list = &view->ssd.titlebar.active.parts;
/* extents */
} else if (node->parent == &view->ssd.extents.tree->node) {
part_list = &view->ssd.extents.parts;
/* active border */
} else if (node->parent == &view->ssd.border.active.tree->node) {
part_list = &view->ssd.border.active.parts;
/* inactive titlebar */
} else if (node->parent == &view->ssd.titlebar.inactive.tree->node) {
part_list = &view->ssd.titlebar.inactive.parts;
} else if (grandparent == &view->ssd.titlebar.inactive.tree->node) {
part_list = &view->ssd.titlebar.inactive.parts;
/* inactive border */
} else if (node->parent == &view->ssd.border.inactive.tree->node) {
part_list = &view->ssd.border.inactive.parts;
}
if (part_list) {
struct ssd_part *part;
wl_list_for_each(part, part_list, link) {
if (node == part->node) {
return part->type;
}
}
}
return LAB_SSD_NONE;
}
enum ssd_part_type
ssd_at(struct view *view, double lx, double ly)
{
double sx, sy;
struct wlr_scene_node *node = wlr_scene_node_at(
&view->server->scene->node, lx, ly, &sx, &sy);
return ssd_get_part_type(view, node);
}
uint32_t
ssd_resize_edges(enum ssd_part_type type)
{
switch (type) {
case LAB_SSD_PART_TOP:
return WLR_EDGE_TOP;
case LAB_SSD_PART_RIGHT:
return WLR_EDGE_RIGHT;
case LAB_SSD_PART_BOTTOM:
return WLR_EDGE_BOTTOM;
case LAB_SSD_PART_LEFT:
return WLR_EDGE_LEFT;
case LAB_SSD_PART_CORNER_TOP_LEFT:
return WLR_EDGE_TOP | WLR_EDGE_LEFT;
case LAB_SSD_PART_CORNER_TOP_RIGHT:
return WLR_EDGE_RIGHT | WLR_EDGE_TOP;
case LAB_SSD_PART_CORNER_BOTTOM_RIGHT:
return WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT;
case LAB_SSD_PART_CORNER_BOTTOM_LEFT:
return WLR_EDGE_BOTTOM | WLR_EDGE_LEFT;
default:
break;
}
return 0;
}
static void
_ssd_set_active(struct ssd *ssd, bool active)
{
wlr_scene_node_set_enabled(&ssd->border.active.tree->node, active);
wlr_scene_node_set_enabled(&ssd->titlebar.active.tree->node, active);
wlr_scene_node_set_enabled(&ssd->border.inactive.tree->node, !active);
wlr_scene_node_set_enabled(&ssd->titlebar.inactive.tree->node, !active);
}
void
ssd_create(struct view *view)
{
if (view->ssd.tree) {
/* SSD was hidden. Just enable it */
wlr_scene_node_set_enabled(&view->ssd.tree->node, true);
return;
}
view->ssd.tree = wlr_scene_tree_create(&view->scene_tree->node);
wlr_scene_node_lower_to_bottom(&view->ssd.tree->node);
ssd_extents_create(view);
ssd_titlebar_create(view);
ssd_border_create(view);
}
void
ssd_update_geometry(struct view *view)
{
/* TODO: verify we are not called without reason. like in commit handlers */
if (!view->ssd.tree || !view->scene_node) {
return;
}
if (view->ssd.enabled && !view->ssd.tree->node.state.enabled) {
wlr_scene_node_set_enabled(&view->ssd.tree->node, true);
}
if (!view->ssd.enabled && view->ssd.tree->node.state.enabled) {
wlr_scene_node_set_enabled(&view->ssd.tree->node, false);
}
int width = view->w;
int height = view->h;
if (width == view->ssd.state.width && height == view->ssd.state.height) {
return;
}
ssd_extents_update(view);
ssd_border_update(view);
ssd_titlebar_update(view);
view->ssd.state.width = width;
view->ssd.state.height = height;
}
void
ssd_hide(struct view *view)
{
if (!view->ssd.tree) {
return;
}
wlr_log(WLR_ERROR, "Hiding SSD");
wlr_scene_node_set_enabled(&view->ssd.tree->node, false);
}
void ssd_reload(struct view *view)
{
if (!view->ssd.tree) {
return;
}
bool view_was_active = view->server->ssd_focused_view == view;
ssd_destroy(view);
ssd_create(view);
if (view_was_active) {
view->server->ssd_focused_view = view;
} else {
_ssd_set_active(&view->ssd, false);
}
}
void
ssd_destroy(struct view *view)
{
if (!view->ssd.tree) {
return;
}
/* Maybe reset focused view */
if (view->server->ssd_focused_view == view) {
view->server->ssd_focused_view = NULL;
}
/* Maybe reset hover view */
struct ssd_hover_state *hover_state;
hover_state = &view->server->ssd_hover_state;
if (hover_state->view == view) {
hover_state->view = NULL;
hover_state->type = LAB_SSD_NONE;
hover_state->node = NULL;
}
/* Destroy subcomponents */
ssd_titlebar_destroy(view);
ssd_border_destroy(view);
ssd_extents_destroy(view);
wlr_scene_node_destroy(&view->ssd.tree->node);
view->ssd.tree = NULL;
}
bool
ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate)
{
if (whole == candidate) {
return true;
}
if (whole == LAB_SSD_PART_TITLEBAR) {
return candidate >= LAB_SSD_BUTTON_CLOSE
&& candidate <= LAB_SSD_PART_TITLE;
}
if (whole == LAB_SSD_FRAME) {
return candidate >= LAB_SSD_BUTTON_CLOSE
&& candidate <= LAB_SSD_CLIENT;
}
if (whole == LAB_SSD_PART_TOP) {
return candidate == LAB_SSD_PART_CORNER_TOP_LEFT
|| candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT;
}
if (whole == LAB_SSD_PART_RIGHT) {
return candidate == LAB_SSD_PART_CORNER_TOP_RIGHT
|| candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT;
}
if (whole == LAB_SSD_PART_BOTTOM) {
return candidate == LAB_SSD_PART_CORNER_BOTTOM_RIGHT
|| candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT;
}
if (whole == LAB_SSD_PART_LEFT) {
return candidate == LAB_SSD_PART_CORNER_TOP_LEFT
|| candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT;
}
return false;
}
void
ssd_set_active(struct view *view)
{
if (!view->ssd.tree) {
return;
}
struct view *last = view->server->ssd_focused_view;
if (last == view) {
return;
}
if (last && last->ssd.tree) {
_ssd_set_active(&last->ssd, false);
}
_ssd_set_active(&view->ssd, true);
view->server->ssd_focused_view = view;
}

106
src/ssd/ssd_border.c Normal file
View file

@ -0,0 +1,106 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "labwc.h"
#include "ssd.h"
#include "theme.h"
#include "common/scene-helpers.h"
#define FOR_EACH_STATE(view, tmp) FOR_EACH(tmp, \
&(view)->ssd.border.active, \
&(view)->ssd.border.inactive)
void
ssd_border_create(struct view *view)
{
struct theme *theme = view->server->theme;
int width = view->w;
int height = view->h;
int full_width = width + 2 * theme->border_width;
float *color;
struct wlr_scene_node *parent;
struct ssd_sub_tree *subtree;
FOR_EACH_STATE(view, subtree) {
subtree->tree = wlr_scene_tree_create(&view->ssd.tree->node);
parent = &subtree->tree->node;
wlr_scene_node_set_position(parent, -theme->border_width, 0);
if (subtree == &view->ssd.border.active) {
color = theme->window_active_border_color;
} else {
color = theme->window_inactive_border_color;
wlr_scene_node_set_enabled(parent, false);
}
wl_list_init(&subtree->parts);
add_scene_rect(&subtree->parts, LAB_SSD_PART_LEFT, parent,
theme->border_width, height, 0, 0, color);
add_scene_rect(&subtree->parts, LAB_SSD_PART_RIGHT, parent,
theme->border_width, height,
theme->border_width + width, 0, color);
add_scene_rect(&subtree->parts, LAB_SSD_PART_BOTTOM, parent,
full_width, theme->border_width,
0, height, color);
add_scene_rect(&subtree->parts, LAB_SSD_PART_TOP, parent,
full_width - 2 * BUTTON_WIDTH, theme->border_width,
BUTTON_WIDTH, -SSD_HEIGHT, color);
} FOR_EACH_END
}
void
ssd_border_update(struct view *view)
{
struct theme *theme = view->server->theme;
int width = view->w;
int height = view->h;
int full_width = width + 2 * theme->border_width;
struct ssd_part *part;
struct wlr_scene_rect *rect;
struct ssd_sub_tree *subtree;
FOR_EACH_STATE(view, subtree) {
wl_list_for_each(part, &subtree->parts, link) {
rect = lab_wlr_scene_get_rect(part->node);
switch (part->type) {
case LAB_SSD_PART_LEFT:
wlr_scene_rect_set_size(rect, theme->border_width, height);
continue;
case LAB_SSD_PART_RIGHT:
wlr_scene_rect_set_size(rect, theme->border_width, height);
wlr_scene_node_set_position(
part->node, theme->border_width + width, 0);
continue;
case LAB_SSD_PART_BOTTOM:
wlr_scene_rect_set_size(rect, full_width, theme->border_width);
wlr_scene_node_set_position(part->node, 0, height);
continue;
case LAB_SSD_PART_TOP:
wlr_scene_rect_set_size(rect,
full_width - 2 * BUTTON_WIDTH,
theme->border_width);
wlr_scene_node_set_position(part->node,
BUTTON_WIDTH, -SSD_HEIGHT);
continue;
default:
continue;
}
}
} FOR_EACH_END
}
void
ssd_border_destroy(struct view *view)
{
if (!view->ssd.border.active.tree) {
return;
}
struct ssd_sub_tree *subtree;
FOR_EACH_STATE(view, subtree) {
ssd_destroy_parts(&subtree->parts);
wlr_scene_node_destroy(&subtree->tree->node);
subtree->tree = NULL;
} FOR_EACH_END
}
#undef FOR_EACH_STATE

128
src/ssd/ssd_extents.c Normal file
View file

@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "labwc.h"
#include "ssd.h"
#include "theme.h"
#include "common/scene-helpers.h"
void
ssd_extents_create(struct view *view)
{
struct theme *theme = view->server->theme;
float invisible[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
struct wl_list *part_list = &view->ssd.extents.parts;
int width = view->w;
int height = view->h;
int full_height = height + theme->border_width + SSD_HEIGHT;
int full_width = width + 2 * theme->border_width;
int extended_area = EXTENDED_AREA;
view->ssd.extents.tree = wlr_scene_tree_create(&view->ssd.tree->node);
struct wlr_scene_node *parent = &view->ssd.extents.tree->node;
if (view->maximized || view->fullscreen) {
wlr_scene_node_set_enabled(parent, false);
}
wl_list_init(&view->ssd.extents.parts);
wlr_scene_node_set_position(parent,
-(theme->border_width + extended_area), -(SSD_HEIGHT + extended_area));
/* Top */
add_scene_rect(part_list, LAB_SSD_PART_CORNER_TOP_LEFT, parent,
extended_area, extended_area,
0, 0, invisible);
add_scene_rect(part_list, LAB_SSD_PART_TOP, parent,
full_width, extended_area,
extended_area, 0, invisible);
add_scene_rect(part_list, LAB_SSD_PART_CORNER_TOP_RIGHT, parent,
extended_area, extended_area,
extended_area + full_width, 0, invisible);
/* Sides */
add_scene_rect(part_list, LAB_SSD_PART_LEFT, parent,
extended_area, full_height,
0, extended_area, invisible);
add_scene_rect(part_list, LAB_SSD_PART_RIGHT, parent,
extended_area, full_height,
extended_area + full_width, extended_area, invisible);
/* Bottom */
add_scene_rect(part_list, LAB_SSD_PART_CORNER_BOTTOM_LEFT, parent,
extended_area, extended_area,
0, extended_area + full_height, invisible);
add_scene_rect(part_list, LAB_SSD_PART_BOTTOM, parent,
full_width, extended_area,
extended_area, extended_area + full_height, invisible);
add_scene_rect(part_list, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, parent,
extended_area, extended_area,
extended_area + full_width, extended_area + full_height, invisible);
}
void
ssd_extents_update(struct view *view)
{
if (view->maximized || view->fullscreen) {
wlr_scene_node_set_enabled(&view->ssd.extents.tree->node, false);
return;
}
if (!view->ssd.extents.tree->node.state.enabled) {
wlr_scene_node_set_enabled(&view->ssd.extents.tree->node, true);
}
struct theme *theme = view->server->theme;
int width = view->w;
int height = view->h;
int full_height = height + theme->border_width + SSD_HEIGHT;
int full_width = width + 2 * theme->border_width;
int extended_area = EXTENDED_AREA;
struct ssd_part *part;
struct wlr_scene_rect *rect;
wl_list_for_each(part, &view->ssd.extents.parts, link) {
rect = lab_wlr_scene_get_rect(part->node);
switch (part->type) {
case LAB_SSD_PART_TOP:
wlr_scene_rect_set_size(rect, full_width, extended_area);
continue;
case LAB_SSD_PART_CORNER_TOP_RIGHT:
wlr_scene_node_set_position(
part->node, extended_area + full_width, 0);
continue;
case LAB_SSD_PART_LEFT:
wlr_scene_rect_set_size(rect, extended_area, full_height);
continue;
case LAB_SSD_PART_RIGHT:
wlr_scene_rect_set_size(rect, extended_area, full_height);
wlr_scene_node_set_position(
part->node, extended_area + full_width, extended_area);
continue;
case LAB_SSD_PART_CORNER_BOTTOM_LEFT:
wlr_scene_node_set_position(
part->node, 0, extended_area + full_height);
continue;
case LAB_SSD_PART_BOTTOM:
wlr_scene_rect_set_size(rect, full_width, extended_area);
wlr_scene_node_set_position(
part->node, extended_area, extended_area + full_height);
continue;
case LAB_SSD_PART_CORNER_BOTTOM_RIGHT:
wlr_scene_node_set_position(part->node,
extended_area + full_width, extended_area + full_height);
continue;
default:
continue;
}
}
}
void
ssd_extents_destroy(struct view *view)
{
if (!view->ssd.extents.tree) {
return;
}
ssd_destroy_parts(&view->ssd.extents.parts);
wlr_scene_node_destroy(&view->ssd.extents.tree->node);
view->ssd.extents.tree = NULL;
}

118
src/ssd/ssd_part.c Normal file
View file

@ -0,0 +1,118 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include "labwc.h"
#include "ssd.h"
struct ssd_part *
add_scene_part(struct wl_list *part_list, enum ssd_part_type type)
{
struct ssd_part *part = calloc(1, sizeof(struct ssd_part));
part->type = type;
wl_list_insert(part_list->prev, &part->link);
return part;
}
struct ssd_part *
add_scene_rect(struct wl_list *list, enum ssd_part_type type,
struct wlr_scene_node *parent, int width, int height,
int x, int y, float color[4])
{
/*
* When initialized without surface being mapped,
* size may be negative. Just set to 0, next call
* to ssd_*_update() will update the rect to use
* its correct size.
*/
width = width >= 0 ? width : 0;
height = height >= 0 ? height : 0;
struct ssd_part *part = add_scene_part(list, type);
part->node = &wlr_scene_rect_create(
parent, width, height, color)->node;
wlr_scene_node_set_position(part->node, x, y);
return part;
}
struct ssd_part *
add_scene_buffer(struct wl_list *list, enum ssd_part_type type,
struct wlr_scene_node *parent, struct wlr_buffer *buffer,
int x, int y)
{
struct ssd_part *part = add_scene_part(list, type);
part->node = &wlr_scene_buffer_create(parent, buffer)->node;
wlr_scene_node_set_position(part->node, x, y);
return part;
}
static void
finish_scene_button(struct wl_list *part_list, enum ssd_part_type type,
struct wlr_scene_node *parent, struct wlr_buffer *icon_buffer)
{
float hover_bg[4] = {0.15f, 0.15f, 0.15f, 0.3f};
/* Icon */
add_scene_buffer(part_list, type, parent, icon_buffer,
(BUTTON_WIDTH - icon_buffer->width) / 2,
(SSD_HEIGHT - icon_buffer->height) / 2);
/* Hover overlay */
struct ssd_part *hover_part;
hover_part = add_scene_rect(part_list, type, parent,
BUTTON_WIDTH, SSD_HEIGHT, 0, 0, hover_bg);
wlr_scene_node_set_enabled(hover_part->node, false);
}
struct ssd_part *
add_scene_button_corner(struct wl_list *part_list, enum ssd_part_type type,
struct wlr_scene_node *parent, struct wlr_buffer *corner_buffer,
struct wlr_buffer *icon_buffer, int x)
{
struct ssd_part *part;
/* Background */
part = add_scene_buffer(part_list, type, parent, corner_buffer, x, 0);
finish_scene_button(part_list, type, part->node, icon_buffer);
return part;
}
struct ssd_part *
add_scene_button(struct wl_list *part_list, enum ssd_part_type type,
struct wlr_scene_node *parent, float *bg_color,
struct wlr_buffer *icon_buffer, int x)
{
struct ssd_part *part;
/* Background */
part = add_scene_rect(part_list, type, parent,
BUTTON_WIDTH, SSD_HEIGHT, x, 0, bg_color);
finish_scene_button(part_list, type, part->node, icon_buffer);
return part;
}
struct ssd_part *
ssd_get_part(struct wl_list *part_list, enum ssd_part_type type)
{
struct ssd_part *part;
wl_list_for_each(part, part_list, link) {
if (part->type == type) {
return part;
}
}
return NULL;
}
void
ssd_destroy_parts(struct wl_list *list)
{
struct ssd_part *part, *tmp;
wl_list_for_each_reverse_safe(part, tmp, list, link) {
if (part->node) {
wlr_scene_node_destroy(part->node);
}
if (part->buffer) {
wlr_buffer_drop(&part->buffer->base);
}
wl_list_remove(&part->link);
free(part);
}
assert(wl_list_empty(list));
}

304
src/ssd/ssd_titlebar.c Normal file
View file

@ -0,0 +1,304 @@
// SPDX-License-Identifier: GPL-2.0-only
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <string.h>
#include "labwc.h"
#include "ssd.h"
#include "theme.h"
#include "common/font.h"
#include "common/scene-helpers.h"
#define FOR_EACH_STATE(view, tmp) FOR_EACH(tmp, \
&(view)->ssd.titlebar.active, \
&(view)->ssd.titlebar.inactive)
void
ssd_titlebar_create(struct view *view)
{
struct theme *theme = view->server->theme;
int width = view->w;
int full_width = width + 2 * theme->border_width;
float *color;
struct wlr_scene_node *parent;
struct wlr_buffer *corner_top_left;
struct wlr_buffer *corner_top_right;
struct ssd_sub_tree *subtree;
FOR_EACH_STATE(view, subtree) {
subtree->tree = wlr_scene_tree_create(&view->ssd.tree->node);
parent = &subtree->tree->node;
wlr_scene_node_set_position(parent, -theme->border_width, -SSD_HEIGHT);
if (subtree == &view->ssd.titlebar.active) {
color = theme->window_active_title_bg_color;
corner_top_left = &theme->corner_top_left_active_normal->base;
corner_top_right = &theme->corner_top_right_active_normal->base;
} else {
color = theme->window_inactive_title_bg_color;
corner_top_left = &theme->corner_top_left_inactive_normal->base;
corner_top_right = &theme->corner_top_right_inactive_normal->base;
wlr_scene_node_set_enabled(parent, false);
}
wl_list_init(&subtree->parts);
/* Title */
add_scene_rect(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent,
full_width - BUTTON_WIDTH * BUTTON_COUNT, SSD_HEIGHT,
BUTTON_WIDTH, 0, color);
/* Buttons */
add_scene_button_corner(&subtree->parts, LAB_SSD_BUTTON_WINDOW_MENU,
parent, corner_top_left,
&theme->xbm_menu_active_unpressed->base, 0);
add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent,
color, &theme->xbm_iconify_active_unpressed->base,
full_width - BUTTON_WIDTH * 3);
add_scene_button(&subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, parent,
color, &theme->xbm_maximize_active_unpressed->base,
full_width - BUTTON_WIDTH * 2);
add_scene_button_corner(&subtree->parts, LAB_SSD_BUTTON_CLOSE, parent,
corner_top_right, &theme->xbm_close_active_unpressed->base,
full_width - BUTTON_WIDTH * 1);
} FOR_EACH_END
ssd_update_title(view);
}
static bool
is_direct_child(struct wlr_scene_node *node, struct ssd_sub_tree *subtree)
{
return node->parent == &subtree->tree->node;
}
void
ssd_titlebar_update(struct view *view)
{
int width = view->w;
if (width == view->ssd.state.width) {
return;
}
int full_width = width + 2 * view->server->theme->border_width;
struct ssd_part *part;
struct ssd_sub_tree *subtree;
FOR_EACH_STATE(view, subtree) {
wl_list_for_each(part, &subtree->parts, link) {
switch (part->type) {
case LAB_SSD_PART_TITLEBAR:
wlr_scene_rect_set_size(lab_wlr_scene_get_rect(part->node),
full_width - BUTTON_WIDTH * BUTTON_COUNT, SSD_HEIGHT);
continue;
case LAB_SSD_BUTTON_ICONIFY:
if (is_direct_child(part->node, subtree)) {
wlr_scene_node_set_position(part->node,
full_width - BUTTON_WIDTH * 3, 0);
}
continue;
case LAB_SSD_BUTTON_MAXIMIZE:
if (is_direct_child(part->node, subtree)) {
wlr_scene_node_set_position(part->node,
full_width - BUTTON_WIDTH * 2, 0);
}
continue;
case LAB_SSD_BUTTON_CLOSE:
if (is_direct_child(part->node, subtree)) {
wlr_scene_node_set_position(part->node,
full_width - BUTTON_WIDTH * 1, 0);
}
continue;
default:
continue;
}
}
} FOR_EACH_END
ssd_update_title(view);
}
void
ssd_titlebar_destroy(struct view *view)
{
if (!view->ssd.titlebar.active.tree) {
return;
}
struct ssd_sub_tree *subtree;
FOR_EACH_STATE(view, subtree) {
ssd_destroy_parts(&subtree->parts);
wlr_scene_node_destroy(&subtree->tree->node);
subtree->tree = NULL;
} FOR_EACH_END
if (view->ssd.state.title.text) {
free(view->ssd.state.title.text);
view->ssd.state.title.text = NULL;
}
}
/*
* For ssd_update_title* we do not early out because
* .active and .inactive may result in different sizes
* of the title (font family/size) or background of
* the title (different button/border width).
*/
static void
ssd_update_title_positions(struct view *view)
{
struct theme *theme = view->server->theme;
int width = view->w;
int full_width = width + 2 * view->server->theme->border_width;
int x, y;
struct wlr_scene_rect *rect;
struct ssd_part *part;
struct ssd_sub_tree *subtree;
FOR_EACH_STATE(view, subtree) {
part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE);
if (!part) {
/* view->surface never been mapped */
continue;
}
x = 0;
y = (SSD_HEIGHT - part->buffer->base.height) / 2;
rect = lab_wlr_scene_get_rect(part->node->parent);
if (rect->width <= 0) {
wlr_scene_node_set_position(part->node, x, y);
continue;
}
if (theme->window_label_text_justify == LAB_JUSTIFY_CENTER) {
if (part->buffer->base.width + BUTTON_WIDTH * 2 <= rect->width) {
/* Center based on the full width */
x = (full_width - part->buffer->base.width) / 2;
x -= BUTTON_WIDTH;
} else {
/*
* Center based on the width between the buttons.
* Title jumps around once this is hit but its still
* better than to hide behind the buttons on the right.
*/
x = (rect->width - part->buffer->base.width) / 2;
}
} else if (theme->window_label_text_justify == LAB_JUSTIFY_RIGHT) {
x = rect->width - part->buffer->base.width;
} else if (theme->window_label_text_justify == LAB_JUSTIFY_LEFT) {
/* TODO: maybe add some theme x padding here? */
}
wlr_scene_node_set_position(part->node, x, y);
} FOR_EACH_END
}
void
ssd_update_title(struct view *view)
{
if (!view->ssd.tree) {
return;
}
char *title = (char *)view_get_string_prop(view, "title");
if (!title || !*title) {
return;
}
struct theme *theme = view->server->theme;
struct ssd_state_title *state = &view->ssd.state.title;
bool title_unchanged = state->text && !strcmp(title, state->text);
/* TODO: Do we only have active window fonts? */
struct font font = {
.name = rc.font_name_activewindow,
.size = rc.font_size_activewindow,
};
float *text_color;
struct wlr_scene_rect *rect;
struct ssd_part *part;
struct ssd_part *parent_part;
struct ssd_sub_tree *subtree;
struct ssd_state_title_width *dstate;
FOR_EACH_STATE(view, subtree) {
parent_part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR);
assert(parent_part);
if (subtree == &view->ssd.titlebar.active) {
dstate = &state->active;
text_color = theme->window_active_label_text_color;
} else {
dstate = &state->inactive;
text_color = theme->window_inactive_label_text_color;
}
rect = lab_wlr_scene_get_rect(parent_part->node);
if (rect->width <= 0) {
dstate->truncated = true;
continue;
}
if (title_unchanged
&& !dstate->truncated && dstate->width < rect->width) {
/* title the same + we don't need to resize title */
continue;
}
part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLE);
if (!part) {
part = add_scene_part(&subtree->parts, LAB_SSD_PART_TITLE);
}
if (part->node) {
wlr_scene_node_destroy(part->node);
}
font_buffer_update(
&part->buffer, rect->width, title, &font, text_color);
part->node = &wlr_scene_buffer_create(
parent_part->node, &part->buffer->base)->node;
dstate->width = part->buffer->base.width;
dstate->truncated = rect->width <= dstate->width;
} FOR_EACH_END
if (!title_unchanged) {
if (state->text) {
free(state->text);
}
state->text = strdup(title);
}
ssd_update_title_positions(view);
}
/*
* Returns the wlr_scene_node for hover effect.
* To disable the hover effect later on just call
* wlr_scene_node_set_enabled(node, false).
*/
struct wlr_scene_node *
ssd_button_hover_enable(struct view *view, enum ssd_part_type type)
{
if (!view->ssd.tree) {
wlr_log(WLR_ERROR, "%s() for destroyed view", __func__);
return NULL;
}
assert(ssd_is_button(type));
struct ssd_part *part;
struct ssd_sub_tree *subtree;
FOR_EACH_STATE(view, subtree) {
if (subtree->tree->node.state.enabled) {
part = ssd_get_part(&subtree->parts, type);
if (!part) {
wlr_log(WLR_ERROR, "hover enable failed to find button");
return NULL;
}
struct wlr_scene_node *child;
wl_list_for_each(child, &part->node->state.children, state.link) {
if (child->type == WLR_SCENE_NODE_RECT) {
wlr_scene_node_set_enabled(child, true);
return child;
}
}
}
} FOR_EACH_END
wlr_log(WLR_ERROR, "hover enable failed to find button");
return NULL;
}
#undef FOR_EACH_STATE

View file

@ -1,36 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020 the sway authors
* This file is only needed in support of tracking damage
*/
#include "labwc.h"
static void
subsurface_handle_destroy(struct wl_listener *listener, void *data)
{
struct view_subsurface *subsurface = wl_container_of(listener,
subsurface, destroy);
struct view_child *view_child = (struct view_child *)subsurface;
if (!view_child) {
return;
}
wl_list_remove(&subsurface->destroy.link);
view_child_finish(&subsurface->view_child);
free(subsurface);
}
void
view_subsurface_create(struct view *view, struct wlr_subsurface *wlr_subsurface)
{
struct view_subsurface *subsurface =
calloc(1, sizeof(struct view_subsurface));
if (!subsurface) {
return;
}
view_child_init(&subsurface->view_child, view, wlr_subsurface->surface);
subsurface->subsurface = wlr_subsurface;
subsurface->destroy.notify = subsurface_handle_destroy;
wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
}

View file

@ -25,6 +25,8 @@
#include "config/rcxml.h"
#include "theme.h"
#include "xbm/xbm.h"
#include "buffer.h"
#include "ssd.h"
static int
hex_to_dec(char c)
@ -336,8 +338,8 @@ set_source(cairo_t *cairo, float *c)
cairo_set_source_rgba(cairo, c[0], c[1], c[2], c[3]);
}
static struct wlr_texture *
rounded_rect(struct wlr_renderer *renderer, struct rounded_corner_ctx *ctx)
static struct lab_data_buffer *
rounded_rect(struct rounded_corner_ctx *ctx)
{
/* 1 degree in radians (=2π/360) */
double deg = 0.017453292519943295;
@ -350,9 +352,12 @@ rounded_rect(struct wlr_renderer *renderer, struct rounded_corner_ctx *ctx)
double h = ctx->box->height;
double r = ctx->radius;
cairo_surface_t *surf =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
cairo_t *cairo = cairo_create(surf);
struct lab_data_buffer *buffer;
/* TODO: scale */
buffer = buffer_create_cairo(w, h, 1, true);
cairo_t *cairo = buffer->cairo;
cairo_surface_t *surf = cairo_get_target(cairo);
/* set transparent background */
cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
@ -405,28 +410,19 @@ rounded_rect(struct wlr_renderer *renderer, struct rounded_corner_ctx *ctx)
wlr_log(WLR_ERROR, "unknown corner type");
}
cairo_stroke(cairo);
/* convert to wlr_texture */
cairo_surface_flush(surf);
unsigned char *data = cairo_image_surface_get_data(surf);
struct wlr_texture *texture = wlr_texture_from_pixels(renderer,
DRM_FORMAT_ARGB8888, cairo_image_surface_get_stride(surf),
w, h, data);
cairo_destroy(cairo);
cairo_surface_destroy(surf);
return texture;
return buffer;
}
static void
create_corners(struct theme *theme, struct wlr_renderer *renderer)
create_corners(struct theme *theme)
{
int corner_square = theme->title_height + theme->border_width;
struct wlr_box box = {
.x = 0,
.y = 0,
.width = corner_square,
.height = corner_square,
.width = BUTTON_WIDTH,
.height = SSD_HEIGHT,
};
struct rounded_corner_ctx ctx = {
@ -437,20 +433,20 @@ create_corners(struct theme *theme, struct wlr_renderer *renderer)
.border_color = theme->window_active_border_color,
.corner = LAB_CORNER_TOP_LEFT,
};
theme->corner_top_left_active_normal = rounded_rect(renderer, &ctx);
theme->corner_top_left_active_normal = rounded_rect(&ctx);
ctx.fill_color = theme->window_inactive_title_bg_color,
ctx.border_color = theme->window_inactive_border_color,
theme->corner_top_left_inactive_normal = rounded_rect(renderer, &ctx);
theme->corner_top_left_inactive_normal = rounded_rect(&ctx);
ctx.corner = LAB_CORNER_TOP_RIGHT;
ctx.fill_color = theme->window_active_title_bg_color,
ctx.border_color = theme->window_active_border_color,
theme->corner_top_right_active_normal = rounded_rect(renderer, &ctx);
theme->corner_top_right_active_normal = rounded_rect(&ctx);
ctx.fill_color = theme->window_inactive_title_bg_color,
ctx.border_color = theme->window_inactive_border_color,
theme->corner_top_right_inactive_normal = rounded_rect(renderer, &ctx);
theme->corner_top_right_inactive_normal = rounded_rect(&ctx);
}
static void
@ -480,8 +476,7 @@ post_processing(struct theme *theme)
}
void
theme_init(struct theme *theme, struct wlr_renderer *renderer,
const char *theme_name)
theme_init(struct theme *theme, const char *theme_name)
{
/*
* Set some default values. This is particularly important on
@ -491,8 +486,8 @@ theme_init(struct theme *theme, struct wlr_renderer *renderer,
theme_read(theme, theme_name);
post_processing(theme);
create_corners(theme, renderer);
xbm_load(theme, renderer);
create_corners(theme);
xbm_load(theme);
}
void

View file

@ -1,42 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020 the sway authors
* This file is only needed in support of tracking damage
*/
#include "labwc.h"
static void
view_child_handle_commit(struct wl_listener *listener, void *data)
{
struct view_child *child = wl_container_of(listener, child, commit);
damage_all_outputs(child->parent->server);
}
static void
view_child_handle_new_subsurface(struct wl_listener *listener, void *data)
{
struct view_child *child;
child = wl_container_of(listener, child, new_subsurface);
struct wlr_subsurface *wlr_subsurface = data;
view_subsurface_create(child->parent, wlr_subsurface);
}
void
view_child_finish(struct view_child *child)
{
wl_list_remove(&child->commit.link);
wl_list_remove(&child->new_subsurface.link);
}
void
view_child_init(struct view_child *child, struct view *view,
struct wlr_surface *wlr_surface)
{
child->parent = view;
child->surface = wlr_surface;
child->commit.notify = view_child_handle_commit;
wl_signal_add(&wlr_surface->events.commit, &child->commit);
child->new_subsurface.notify = view_child_handle_new_subsurface;
wl_signal_add(&wlr_surface->events.new_subsurface, &child->new_subsurface);
}

View file

@ -12,6 +12,4 @@ view_impl_map(struct view *view)
view_update_title(view);
view_update_app_id(view);
damage_all_outputs(view->server);
}

View file

@ -7,6 +7,9 @@
void
view_set_activated(struct view *view, bool activated)
{
if (view->ssd.enabled) {
ssd_set_active(view);
}
if (view->impl->set_activated) {
view->impl->set_activated(view, activated);
}
@ -31,6 +34,7 @@ view_move(struct view *view, double x, double y)
view->impl->move(view, x, y);
}
view_discover_output(view);
wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y);
}
void
@ -254,7 +258,7 @@ view_toggle_decorations(struct view *view)
{
if (!view->fullscreen) {
view->ssd.enabled = !view->ssd.enabled;
ssd_update_geometry(view, true);
ssd_update_geometry(view);
if (view->maximized) {
view_apply_maximized_geometry(view);
}
@ -266,7 +270,7 @@ view_set_decorations(struct view *view, bool decorations)
{
if (view->ssd.enabled != decorations && !view->fullscreen) {
view->ssd.enabled = decorations;
ssd_update_geometry(view, true);
ssd_update_geometry(view);
if (view->maximized) {
view_apply_maximized_geometry(view);
}
@ -341,24 +345,6 @@ view_adjust_for_layout_change(struct view *view)
}
}
void
view_for_each_surface(struct view *view, wlr_surface_iterator_func_t iterator,
void *user_data)
{
if (view->impl->for_each_surface) {
view->impl->for_each_surface(view, iterator, user_data);
}
}
void
view_for_each_popup_surface(struct view *view,
wlr_surface_iterator_func_t iterator, void *data)
{
if (view->impl->for_each_popup_surface) {
view->impl->for_each_popup_surface(view, iterator, data);
}
}
struct border
view_border(struct view *view)
{
@ -371,27 +357,9 @@ view_border(struct view *view)
return border;
}
static void
surface_enter_for_each_surface(struct wlr_surface *surface, int sx, int sy,
void *user_data)
{
struct wlr_output *wlr_output = user_data;
wlr_surface_send_enter(surface, wlr_output);
}
static void
surface_leave_for_each_surface(struct wlr_surface *surface, int sx, int sy,
void *user_data)
{
struct wlr_output *wlr_output = user_data;
wlr_surface_send_leave(surface, wlr_output);
}
static void
view_output_enter(struct view *view, struct wlr_output *wlr_output)
{
view_for_each_surface(view, surface_enter_for_each_surface,
wlr_output);
if (view->toplevel_handle) {
wlr_foreign_toplevel_handle_v1_output_enter(
view->toplevel_handle, wlr_output);
@ -401,8 +369,6 @@ view_output_enter(struct view *view, struct wlr_output *wlr_output)
static void
view_output_leave(struct view *view, struct wlr_output *wlr_output)
{
view_for_each_surface(view, surface_leave_for_each_surface,
wlr_output);
if (view->toplevel_handle) {
wlr_foreign_toplevel_handle_v1_output_leave(
view->toplevel_handle, wlr_output);
@ -641,7 +607,6 @@ view_update_title(struct view *view)
}
ssd_update_title(view);
wlr_foreign_toplevel_handle_v1_set_title(view->toplevel_handle, title);
damage_all_outputs(view->server);
}
void

View file

@ -15,6 +15,7 @@
#include "theme.h"
#include "xbm/parse.h"
#include "xbm/xbm.h"
#include "buffer.h"
/* built-in 6x6 buttons */
char menu_button_normal[] = { 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00 };
@ -23,28 +24,6 @@ char max_button_normal[] = { 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f };
char max_button_toggled[] = { 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f };
char close_button_normal[] = { 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 };
static struct wlr_texture *
texture_from_pixmap(struct wlr_renderer *renderer, struct pixmap *pixmap)
{
if (!pixmap) {
return NULL;
}
return wlr_texture_from_pixels(renderer, DRM_FORMAT_ARGB8888,
pixmap->width * 4, pixmap->width,
pixmap->height, pixmap->data);
}
static struct wlr_texture *
texture_from_builtin(struct wlr_renderer *renderer, const char *button)
{
struct pixmap pixmap = parse_xbm_builtin(button, 6);
struct wlr_texture *texture = texture_from_pixmap(renderer, &pixmap);
if (pixmap.data) {
free(pixmap.data);
}
return texture;
}
static char *
xbm_path(const char *button)
{
@ -55,62 +34,59 @@ xbm_path(const char *button)
}
static void
load_button(struct wlr_renderer *renderer, const char *filename,
struct wlr_texture **texture, char *button)
load_button(const char *filename, struct lab_data_buffer **buffer, char *button)
{
if (*texture) {
wlr_texture_destroy(*texture);
*texture = NULL;
struct pixmap pixmap = {0};
if (*buffer) {
wlr_buffer_drop(&(*buffer)->base);
*buffer = NULL;
}
/* Read file into memory as it's easier to tokenzie that way */
char *buffer = grab_file(xbm_path(filename));
if (!buffer) {
goto out;
char *token_buffer = grab_file(xbm_path(filename));
if (token_buffer) {
struct token *tokens = tokenize_xbm(token_buffer);
free(token_buffer);
pixmap = parse_xbm_tokens(tokens);
if (tokens) {
free(tokens);
}
}
if (!pixmap.data) {
pixmap = parse_xbm_builtin(button, 6);
}
struct token *tokens = tokenize_xbm(buffer);
free(buffer);
struct pixmap pixmap = parse_xbm_tokens(tokens);
*texture = texture_from_pixmap(renderer, &pixmap);
if (tokens) {
free(tokens);
}
if (pixmap.data) {
free(pixmap.data);
}
out:
if (!(*texture)) {
*texture = texture_from_builtin(renderer, button);
}
/* Create buffer with free_on_destroy being true */
*buffer = buffer_create_wrap(pixmap.data, pixmap.width, pixmap.height,
pixmap.width * 4, true);
}
void
xbm_load(struct theme *theme, struct wlr_renderer *r)
xbm_load(struct theme *theme)
{
parse_set_color(theme->window_active_button_menu_unpressed_image_color);
load_button(r, "menu.xbm", &theme->xbm_menu_active_unpressed,
load_button("menu.xbm", &theme->xbm_menu_active_unpressed,
menu_button_normal);
parse_set_color(theme->window_active_button_iconify_unpressed_image_color);
load_button(r, "iconify.xbm", &theme->xbm_iconify_active_unpressed,
load_button("iconify.xbm", &theme->xbm_iconify_active_unpressed,
iconify_button_normal);
parse_set_color(theme->window_active_button_max_unpressed_image_color);
load_button(r, "max.xbm", &theme->xbm_maximize_active_unpressed,
load_button("max.xbm", &theme->xbm_maximize_active_unpressed,
max_button_normal);
parse_set_color(theme->window_active_button_close_unpressed_image_color);
load_button(r, "close.xbm", &theme->xbm_close_active_unpressed,
load_button("close.xbm", &theme->xbm_close_active_unpressed,
close_button_normal);
parse_set_color(theme->window_inactive_button_menu_unpressed_image_color);
load_button(r, "menu.xbm", &theme->xbm_menu_inactive_unpressed,
load_button("menu.xbm", &theme->xbm_menu_inactive_unpressed,
menu_button_normal);
parse_set_color(theme->window_inactive_button_iconify_unpressed_image_color);
load_button(r, "iconify.xbm", &theme->xbm_iconify_inactive_unpressed,
load_button("iconify.xbm", &theme->xbm_iconify_inactive_unpressed,
iconify_button_normal);
parse_set_color(theme->window_inactive_button_max_unpressed_image_color);
load_button(r, "max.xbm", &theme->xbm_maximize_inactive_unpressed,
load_button("max.xbm", &theme->xbm_maximize_inactive_unpressed,
max_button_normal);
parse_set_color(theme->window_inactive_button_close_unpressed_image_color);
load_button(r, "close.xbm", &theme->xbm_close_inactive_unpressed,
load_button("close.xbm", &theme->xbm_close_inactive_unpressed,
close_button_normal);
}

View file

@ -3,47 +3,54 @@
* Copyright (C) 2020 the sway authors
*
* This file is only needed in support of
* - tracking damage
* - unconstraining XDG popups
* - keeping non-layer-shell xdg-popups outside the layers.c code
*/
#include "labwc.h"
#include "node.h"
struct view_child {
struct wlr_surface *surface;
struct view *parent;
};
struct xdg_popup {
struct view_child view_child;
struct wlr_xdg_popup *wlr_popup;
struct wl_listener destroy;
struct wl_listener new_popup;
};
static void
xdg_popup_destroy(struct view_child *view_child)
popup_unconstrain(struct view *view, struct wlr_xdg_popup *popup)
{
if (!view_child) {
return;
}
struct xdg_popup *popup = (struct xdg_popup *)view_child;
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->map.link);
wl_list_remove(&popup->unmap.link);
wl_list_remove(&popup->new_popup.link);
view_child_finish(&popup->view_child);
free(popup);
}
struct server *server = view->server;
struct wlr_box *popup_box = &popup->geometry;
struct wlr_output_layout *output_layout = server->output_layout;
struct wlr_output *wlr_output = wlr_output_layout_output_at(
output_layout, view->x + popup_box->x, view->y + popup_box->y);
static void
handle_xdg_popup_map(struct wl_listener *listener, void *data)
{
struct xdg_popup *popup = wl_container_of(listener, popup, map);
damage_all_outputs(popup->view_child.parent->server);
}
struct wlr_box output_box;
wlr_output_layout_get_box(output_layout, wlr_output, &output_box);
static void
handle_xdg_popup_unmap(struct wl_listener *listener, void *data)
{
struct xdg_popup *popup = wl_container_of(listener, popup, unmap);
damage_all_outputs(popup->view_child.parent->server);
struct wlr_box output_toplevel_box = {
.x = output_box.x - view->x,
.y = output_box.y - view->y,
.width = output_box.width,
.height = output_box.height,
};
wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box);
}
static void
handle_xdg_popup_destroy(struct wl_listener *listener, void *data)
{
struct xdg_popup *popup = wl_container_of(listener, popup, destroy);
struct view_child *view_child = (struct view_child *)popup;
xdg_popup_destroy(view_child);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->new_popup.link);
free(popup);
}
static void
@ -54,28 +61,6 @@ popup_handle_new_xdg_popup(struct wl_listener *listener, void *data)
xdg_popup_create(popup->view_child.parent, wlr_popup);
}
static void
popup_unconstrain(struct xdg_popup *popup)
{
struct view *view = popup->view_child.parent;
struct server *server = view->server;
struct wlr_box *popup_box = &popup->wlr_popup->geometry;
struct wlr_output_layout *output_layout = server->output_layout;
struct wlr_output *wlr_output = wlr_output_layout_output_at(
output_layout, view->x + popup_box->x, view->y + popup_box->y);
struct wlr_box *output_box = wlr_output_layout_get_box(
output_layout, wlr_output);
struct wlr_box output_toplevel_box = {
.x = output_box->x - view->x,
.y = output_box->y - view->y,
.width = output_box->width,
.height = output_box->height,
};
wlr_xdg_popup_unconstrain_from_box(
popup->wlr_popup, &output_toplevel_box);
}
void
xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup)
{
@ -85,16 +70,32 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup)
}
popup->wlr_popup = wlr_popup;
view_child_init(&popup->view_child, view, wlr_popup->base->surface);
popup->view_child.parent = view;
popup->view_child.surface = wlr_popup->base->surface;
popup->destroy.notify = handle_xdg_popup_destroy;
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
popup->map.notify = handle_xdg_popup_map;
wl_signal_add(&wlr_popup->base->events.map, &popup->map);
popup->unmap.notify = handle_xdg_popup_unmap;
wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
popup->new_popup.notify = popup_handle_new_xdg_popup;
wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
popup_unconstrain(popup);
/*
* We must add xdg popups to the scene graph so they get rendered. The
* wlroots scene graph provides a helper for this, but to use it we must
* provide the proper parent scene node of the xdg popup. To enable
* this, we always set the user data field of xdg_surfaces to the
* corresponding scene node.
*/
if (!wlr_surface_is_xdg_surface(wlr_popup->parent)) {
wlr_log(WLR_ERROR, "xdg_surface is not xdg");
return;
}
struct wlr_xdg_surface *parent =
wlr_xdg_surface_from_wlr_surface(wlr_popup->parent);
struct wlr_scene_node *parent_node = parent->surface->data;
wlr_popup->base->surface->data =
wlr_scene_xdg_surface_create(parent_node, wlr_popup->base);
node_descriptor_create(wlr_popup->base->surface->data,
LAB_NODE_DESC_XDG_POPUP, view);
popup_unconstrain(view, wlr_popup);
}

126
src/xdg.c
View file

@ -1,12 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include "labwc.h"
#include "node.h"
#include "ssd.h"
/*
* xdg_popup_create() and view_subsurface_create() are only called for the
* purposes of tracking damage.
*/
static void
handle_new_xdg_popup(struct wl_listener *listener, void *data)
{
@ -15,14 +12,6 @@ handle_new_xdg_popup(struct wl_listener *listener, void *data)
xdg_popup_create(view, wlr_popup);
}
static void
new_subsurface_notify(struct wl_listener *listener, void *data)
{
struct view *view = wl_container_of(listener, view, new_subsurface);
struct wlr_subsurface *wlr_subsurface = data;
view_subsurface_create(view, wlr_subsurface);
}
static bool
has_ssd(struct view *view)
{
@ -70,8 +59,8 @@ handle_commit(struct wl_listener *listener, void *data)
view->pending_move_resize.configure_serial = 0;
}
}
ssd_update_geometry(view, false);
damage_view_part(view);
wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y);
ssd_update_geometry(view);
}
static void
@ -98,7 +87,11 @@ handle_destroy(struct wl_listener *listener, void *data)
interactive_end(view);
wl_list_remove(&view->link);
wl_list_remove(&view->destroy.link);
ssd_destroy(view);
if (view->scene_tree) {
ssd_destroy(view);
wlr_scene_node_destroy(&view->scene_tree->node);
view->scene_tree = NULL;
}
free(view);
}
@ -139,29 +132,23 @@ static void
handle_request_minimize(struct wl_listener *listener, void *data)
{
struct view *view = wl_container_of(listener, view, request_minimize);
struct wlr_xdg_surface *surface = data;
if (view) {
view_minimize(view, surface->toplevel->requested.minimized);
}
view_minimize(view, view->xdg_surface->toplevel->requested.minimized);
}
static void
handle_request_maximize(struct wl_listener *listener, void *data)
{
struct view *view = wl_container_of(listener, view, request_maximize);
struct wlr_xdg_surface *surface = data;
if (view) {
view_maximize(view, surface->toplevel->requested.maximized);
}
view_maximize(view, view->xdg_surface->toplevel->requested.maximized);
}
static void
handle_request_fullscreen(struct wl_listener *listener, void *data)
{
struct view *view = wl_container_of(listener, view, request_fullscreen);
struct wlr_xdg_toplevel_set_fullscreen_event *e = data;
view_set_fullscreen(view, e->fullscreen, e->output);
view_set_fullscreen(view,
view->xdg_surface->toplevel->requested.fullscreen,
view->xdg_surface->toplevel->requested.fullscreen_output);
}
static void
@ -194,15 +181,16 @@ xdg_toplevel_view_configure(struct view *view, struct wlr_box geo)
view->pending_move_resize.width = MAX(geo.width, min_width);
view->pending_move_resize.height = MAX(geo.height, min_height);
uint32_t serial = wlr_xdg_toplevel_set_size(view->xdg_surface,
uint32_t serial = wlr_xdg_toplevel_set_size(view->xdg_surface->toplevel,
(uint32_t)geo.width, (uint32_t)geo.height);
if (serial > 0) {
view->pending_move_resize.configure_serial = serial;
} else if (view->pending_move_resize.configure_serial == 0) {
view->x = geo.x;
view->y = geo.y;
ssd_update_geometry(view, false);
damage_all_outputs(view->server);
wlr_scene_node_set_position(&view->scene_tree->node,
view->x, view->y);
ssd_update_geometry(view);
}
}
#undef MAX
@ -212,28 +200,12 @@ xdg_toplevel_view_move(struct view *view, double x, double y)
{
view->x = x;
view->y = y;
ssd_update_geometry(view, false);
damage_all_outputs(view->server);
}
static void
xdg_toplevel_view_close(struct view *view)
{
wlr_xdg_toplevel_send_close(view->xdg_surface);
}
static void
xdg_toplevel_view_for_each_popup_surface(struct view *view,
wlr_surface_iterator_func_t iterator, void *data)
{
wlr_xdg_surface_for_each_popup_surface(view->xdg_surface, iterator, data);
}
static void
xdg_toplevel_view_for_each_surface(struct view *view,
wlr_surface_iterator_func_t iterator, void *data)
{
wlr_xdg_surface_for_each_surface(view->xdg_surface, iterator, data);
wlr_xdg_toplevel_send_close(view->xdg_surface->toplevel);
}
static void
@ -248,7 +220,7 @@ update_padding(struct view *view)
static void
xdg_toplevel_view_maximize(struct view *view, bool maximized)
{
wlr_xdg_toplevel_set_maximized(view->xdg_surface, maximized);
wlr_xdg_toplevel_set_maximized(view->xdg_surface->toplevel, maximized);
}
static void
@ -256,14 +228,14 @@ xdg_toplevel_view_set_activated(struct view *view, bool activated)
{
struct wlr_xdg_surface *surface = view->xdg_surface;
if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
wlr_xdg_toplevel_set_activated(surface, activated);
wlr_xdg_toplevel_set_activated(surface->toplevel, activated);
}
}
static void
xdg_toplevel_view_set_fullscreen(struct view *view, bool fullscreen)
{
wlr_xdg_toplevel_set_fullscreen(view->xdg_surface, fullscreen);
wlr_xdg_toplevel_set_fullscreen(view->xdg_surface->toplevel, fullscreen);
}
static bool
@ -277,7 +249,8 @@ parent_of(struct view *view)
{
struct view *p;
wl_list_for_each (p, &view->server->views, link) {
if (p->xdg_surface == view->xdg_surface->toplevel->parent) {
if (p->xdg_surface->toplevel
== view->xdg_surface->toplevel->parent) {
return p;
}
}
@ -328,8 +301,12 @@ xdg_toplevel_view_get_string_prop(struct view *view, const char *prop)
static void
xdg_toplevel_view_map(struct view *view)
{
if (view->mapped) {
return;
}
view->mapped = true;
view->surface = view->xdg_surface->surface;
wlr_scene_node_set_enabled(&view->scene_tree->node, true);
if (!view->been_mapped) {
struct wlr_xdg_toplevel_requested *requested =
&view->xdg_surface->toplevel->requested;
@ -352,16 +329,6 @@ xdg_toplevel_view_map(struct view *view)
if (!view->maximized && !view->fullscreen) {
position_xdg_toplevel_view(view);
}
struct wlr_subsurface *subsurface;
wl_list_for_each(subsurface, &view->surface->current.subsurfaces_below,
current.link) {
view_subsurface_create(view, subsurface);
}
wl_list_for_each(subsurface, &view->surface->current.subsurfaces_above,
current.link) {
view_subsurface_create(view, subsurface);
}
view_discover_output(view);
view->been_mapped = true;
@ -370,9 +337,6 @@ xdg_toplevel_view_map(struct view *view)
view->commit.notify = handle_commit;
wl_signal_add(&view->xdg_surface->surface->events.commit,
&view->commit);
view->new_subsurface.notify = new_subsurface_notify;
wl_signal_add(&view->surface->events.new_subsurface,
&view->new_subsurface);
view_impl_map(view);
}
@ -382,9 +346,8 @@ xdg_toplevel_view_unmap(struct view *view)
{
if (view->mapped) {
view->mapped = false;
damage_all_outputs(view->server);
wlr_scene_node_set_enabled(&view->scene_tree->node, false);
wl_list_remove(&view->commit.link);
wl_list_remove(&view->new_subsurface.link);
desktop_focus_topmost_mapped_view(view->server);
}
}
@ -392,8 +355,6 @@ xdg_toplevel_view_unmap(struct view *view)
static const struct view_impl xdg_toplevel_view_impl = {
.configure = xdg_toplevel_view_configure,
.close = xdg_toplevel_view_close,
.for_each_popup_surface = xdg_toplevel_view_for_each_popup_surface,
.for_each_surface = xdg_toplevel_view_for_each_surface,
.get_string_prop = xdg_toplevel_view_get_string_prop,
.map = xdg_toplevel_view_map,
.move = xdg_toplevel_view_move,
@ -403,15 +364,28 @@ static const struct view_impl xdg_toplevel_view_impl = {
.maximize = xdg_toplevel_view_maximize,
};
/*
* We use the following struct user_data pointers:
* - wlr_xdg_surface->data = view
* for the wlr_xdg_toplevel_decoration_v1 implementation
* - wlr_surface->data = scene_node
* to help the popups find their parent nodes
*/
void
xdg_surface_new(struct wl_listener *listener, void *data)
{
struct server *server =
wl_container_of(listener, server, new_xdg_surface);
struct wlr_xdg_surface *xdg_surface = data;
/*
* We deal with popups in xdg-popup.c and layers.c as they have to be
* treated differently
*/
if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
return;
}
wlr_xdg_surface_ping(xdg_surface);
struct view *view = calloc(1, sizeof(struct view));
@ -419,10 +393,26 @@ xdg_surface_new(struct wl_listener *listener, void *data)
view->type = LAB_XDG_SHELL_VIEW;
view->impl = &xdg_toplevel_view_impl;
view->xdg_surface = xdg_surface;
wl_list_init(&view->ssd.parts);
view->scene_tree = wlr_scene_tree_create(&view->server->view_tree->node);
wlr_scene_node_set_enabled(&view->scene_tree->node, false);
view->scene_node = wlr_scene_xdg_surface_create(
&view->scene_tree->node, view->xdg_surface);
if (!view->scene_node) {
/* TODO: might need further clean up */
wl_resource_post_no_memory(view->surface->resource);
return;
}
node_descriptor_create(&view->scene_tree->node,
LAB_NODE_DESC_VIEW, view);
/* In support of xdg_toplevel_decoration */
xdg_surface->data = view;
/* In support of xdg popups */
xdg_surface->surface->data = view->scene_node;
view->map.notify = handle_map;
wl_signal_add(&xdg_surface->events.map, &view->map);
view->unmap.notify = handle_unmap;

View file

@ -20,7 +20,22 @@ 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;
damage_all_outputs(unmanaged->server);
}
static struct view *
parent_view(struct server *server, struct wlr_xwayland_surface *surface)
{
struct wlr_xwayland_surface *s = surface;
while (s->parent) {
s = s->parent;
}
struct view *view;
wl_list_for_each(view, &server->views, link) {
if (view->xwayland_surface == s) {
return view;
}
}
return NULL;
}
static void
@ -38,10 +53,24 @@ unmanaged_handle_map(struct wl_listener *listener, void *data)
unmanaged->lx = xsurface->x;
unmanaged->ly = xsurface->y;
damage_all_outputs(unmanaged->server);
if (wlr_xwayland_or_surface_wants_focus(xsurface)) {
seat_focus_surface(&unmanaged->server->seat, xsurface->surface);
}
int lx = unmanaged->lx;
int ly = unmanaged->ly;
struct wlr_scene_node *parent, *node;
struct view *view = parent_view(unmanaged->server, xsurface);
if (!view || !view->scene_node) {
parent = &unmanaged->server->unmanaged_tree->node;
} else {
lx -= view->x;
ly -= view->y;
parent = &view->scene_tree->node;
}
/* node will be destroyed automatically once surface is destroyed */
node = &wlr_scene_surface_create(parent, xsurface->surface)->node;
wlr_scene_node_set_position(node, lx, ly);
}
static void
@ -50,7 +79,6 @@ unmanaged_handle_unmap(struct wl_listener *listener, void *data)
struct xwayland_unmanaged *unmanaged =
wl_container_of(listener, unmanaged, unmap);
struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface;
damage_all_outputs(unmanaged->server);
wl_list_remove(&unmanaged->link);
wl_list_remove(&unmanaged->commit.link);

View file

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <assert.h>
#include "labwc.h"
#include "node.h"
#include "ssd.h"
static void
@ -10,21 +11,37 @@ handle_commit(struct wl_listener *listener, void *data)
assert(view->surface);
/* Must receive commit signal before accessing surface->current* */
view->w = view->surface->current.width;
view->h = view->surface->current.height;
struct wlr_surface_state *state = &view->surface->current;
struct view_pending_move_resize *pending = &view->pending_move_resize;
if (view->pending_move_resize.update_x) {
view->x = view->pending_move_resize.x +
view->pending_move_resize.width - view->w;
view->pending_move_resize.update_x = false;
bool move_pending = pending->update_x || pending->update_y;
bool size_changed = view->w != state->width || view->h != state->height;
if (!move_pending && !size_changed) {
return;
}
if (view->pending_move_resize.update_y) {
view->y = view->pending_move_resize.y +
view->pending_move_resize.height - view->h;
view->pending_move_resize.update_y = false;
view->w = state->width;
view->h = state->height;
if (pending->update_x) {
/* Adjust x for queued up configure events */
view->x = pending->x + pending->width - view->w;
}
ssd_update_geometry(view, false);
damage_view_whole(view);
if (pending->update_y) {
/* Adjust y for queued up configure events */
view->y = pending->y + pending->height - view->h;
}
if (move_pending) {
wlr_scene_node_set_position(&view->scene_tree->node,
view->x, view->y);
}
if ((int)pending->width == view->w && (int)pending->height == view->h) {
/* We reached the end of all queued size changing configure events */
pending->update_x = false;
pending->update_y = false;
}
ssd_update_geometry(view);
}
static void
@ -90,7 +107,11 @@ handle_destroy(struct wl_listener *listener, void *data)
wl_list_remove(&view->request_configure.link);
wl_list_remove(&view->request_maximize.link);
wl_list_remove(&view->request_fullscreen.link);
ssd_destroy(view);
if (view->scene_tree) {
ssd_destroy(view);
wlr_scene_node_destroy(&view->scene_tree->node);
view->scene_tree = NULL;
}
free(view);
}
@ -107,7 +128,6 @@ handle_request_configure(struct wl_listener *listener, void *data)
wlr_xwayland_surface_configure(view->xwayland_surface,
event->x, event->y, MAX(event->width, min_width),
MAX(event->height, min_height));
damage_all_outputs(view->server);
}
#undef MAX
@ -173,7 +193,6 @@ configure(struct view *view, struct wlr_box geo)
wlr_xwayland_surface_configure(view->xwayland_surface, (int16_t)geo.x,
(int16_t)geo.y, (uint16_t)geo.width,
(uint16_t)geo.height);
damage_all_outputs(view->server);
}
static void
@ -184,25 +203,12 @@ move(struct view *view, double x, double 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);
ssd_update_geometry(view, false);
damage_all_outputs(view->server);
}
static void
_close(struct view *view)
{
wlr_xwayland_surface_close(view->xwayland_surface);
damage_all_outputs(view->server);
}
static void
for_each_surface(struct view *view, wlr_surface_iterator_func_t iterator,
void *data)
{
if (!view->surface) {
return;
}
wlr_surface_for_each_surface(view->surface, iterator, data);
}
static const char *
@ -254,7 +260,11 @@ top_left_edge_boundary_check(struct view *view)
static void
map(struct view *view)
{
if (view->mapped) {
return;
}
view->mapped = true;
wlr_scene_node_set_enabled(&view->scene_tree->node, true);
if (!view->fullscreen && view->xwayland_surface->fullscreen) {
view_set_fullscreen(view, true, NULL);
}
@ -264,7 +274,18 @@ map(struct view *view)
view->w = view->xwayland_surface->width;
view->h = view->xwayland_surface->height;
}
view->surface = view->xwayland_surface->surface;
if (view->surface != view->xwayland_surface->surface) {
view->surface = view->xwayland_surface->surface;
view->scene_node = wlr_scene_subsurface_tree_create(
&view->scene_tree->node, view->surface);
if (!view->scene_node) {
/* TODO: might need further clean up */
wl_resource_post_no_memory(view->surface->resource);
return;
}
}
view->ssd.enabled = want_deco(view);
if (view->ssd.enabled) {
@ -304,7 +325,7 @@ unmap(struct view *view)
{
if (view->mapped) {
view->mapped = false;
damage_all_outputs(view->server);
wlr_scene_node_set_enabled(&view->scene_tree->node, false);
wl_list_remove(&view->commit.link);
desktop_focus_topmost_mapped_view(view->server);
}
@ -338,7 +359,6 @@ set_fullscreen(struct view *view, bool fullscreen)
static const struct view_impl xwl_view_impl = {
.configure = configure,
.close = _close,
.for_each_surface = for_each_surface,
.get_string_prop = get_string_prop,
.map = map,
.move = move,
@ -370,8 +390,10 @@ xwayland_surface_new(struct wl_listener *listener, void *data)
view->type = LAB_XWAYLAND_VIEW;
view->impl = &xwl_view_impl;
view->xwayland_surface = xsurface;
wl_list_init(&view->ssd.parts);
view->scene_tree = wlr_scene_tree_create(&view->server->view_tree->node);
node_descriptor_create(&view->scene_tree->node,
LAB_NODE_DESC_VIEW, view);
xsurface->data = view;
view->map.notify = handle_map;

View file

@ -1,3 +1,3 @@
[wrap-git]
url = https://gitlab.freedesktop.org/wlroots/wlroots.git
revision = 0.15.1
revision = master