From b5db437f0d89404936c654e43f0fce8c4d1e7715 Mon Sep 17 00:00:00 2001 From: Nobuhiko Tanibata Date: Mon, 20 Jan 2014 10:27:29 +0900 Subject: Weston 1.3.92 --- .gitignore | 2 + Makefile.am | 29 +- clients/.gitignore | 6 +- clients/Makefile.am | 56 +- clients/clickdot.c | 2 +- clients/cliptest.c | 2 +- clients/desktop-shell.c | 24 +- clients/dnd.c | 94 +- clients/editor.c | 55 +- clients/eventdemo.c | 2 +- clients/flower.c | 3 +- clients/fullscreen.c | 3 +- clients/gears.c | 2 +- clients/image.c | 4 +- clients/keyboard.c | 86 +- clients/nested.c | 678 ++++- clients/resizor.c | 5 +- clients/scaler.c | 215 ++ clients/simple-egl.c | 107 +- clients/stacking.c | 312 ++ clients/subsurfaces.c | 3 +- clients/tablet-shell.c | 478 ---- clients/terminal.c | 383 ++- clients/transformed.c | 3 +- clients/view.c | 4 +- clients/weston-info.c | 16 +- clients/window.c | 1025 ++++--- clients/window.h | 39 +- clients/wscreensaver.c | 2 +- configure.ac | 111 +- desktop-shell/.gitignore | 4 + desktop-shell/Makefile.am | 39 + desktop-shell/exposay.c | 609 ++++ desktop-shell/input-panel.c | 354 +++ desktop-shell/shell.c | 5641 +++++++++++++++++++++++++++++++++++++ desktop-shell/shell.h | 220 ++ man/Makefile.am | 1 + man/weston.ini.man | 39 +- man/weston.man | 20 +- protocol/Makefile.am | 23 +- protocol/scaler.xml | 156 + protocol/subsurface.xml | 244 -- protocol/tablet-shell.xml | 40 - protocol/wayland-test.xml | 7 + protocol/xdg-shell.xml | 438 +++ shared/Makefile.am | 2 + shared/cairo-util.c | 106 +- shared/cairo-util.h | 126 +- shared/frame.c | 848 ++++++ shared/os-compatibility.c | 19 +- src/.gitignore | 11 +- src/Makefile.am | 133 +- src/animation.c | 253 +- src/bindings.c | 63 +- src/compositor-drm.c | 373 ++- src/compositor-fbdev.c | 25 +- src/compositor-headless.c | 4 - src/compositor-rdp.c | 8 +- src/compositor-rpi.c | 271 +- src/compositor-wayland.c | 1335 +++++++-- src/compositor-x11.c | 214 +- src/compositor.c | 1512 +++++++--- src/compositor.h | 363 ++- src/data-device.c | 408 ++- src/dbus.c | 404 +++ src/dbus.h | 107 + src/evdev.c | 145 +- src/evdev.h | 12 +- src/filter.c | 1 - src/filter.h | 1 - src/gl-renderer.c | 535 ++-- src/gl-renderer.h | 130 +- src/input.c | 724 +++-- src/launcher-util.c | 128 +- src/launcher-util.h | 6 +- src/logind-util.c | 940 ++++++ src/logind-util.h | 120 + src/noop-renderer.c | 13 - src/pixman-renderer.c | 346 +-- src/rpi-bcm-stubs.h | 13 + src/rpi-renderer.c | 700 +++-- src/rpi-renderer.h | 1 + src/screenshooter.c | 110 +- src/shell.c | 4799 ------------------------------- src/spring-tool.c | 4 +- src/tablet-shell.c | 575 ---- src/text-backend.c | 4 +- src/udev-seat.c | 37 +- src/udev-seat.h | 2 +- src/vertex-clipping.c | 70 +- src/vertex-clipping.h | 30 +- src/weston-launch.c | 22 +- src/xwayland/Makefile.am | 40 - src/xwayland/dnd.c | 274 -- src/xwayland/hash.c | 309 -- src/xwayland/hash.h | 49 - src/xwayland/launcher.c | 389 --- src/xwayland/selection.c | 712 ----- src/xwayland/window-manager.c | 2186 -------------- src/xwayland/xwayland.h | 175 -- src/zoom.c | 192 +- tests/.gitignore | 3 +- tests/Makefile.am | 90 +- tests/bad-buffer-test.c | 77 + tests/buffer-count-test.c | 141 + tests/subsurface-test.c | 1 - tests/surface-global-test.c | 27 +- tests/surface-test.c | 17 +- tests/text-test.c | 2 +- tests/vertex-clip-test.c | 12 +- tests/weston-test-client-helper.c | 36 +- tests/weston-test-client-helper.h | 7 + tests/weston-test.c | 97 +- weston.ini | 66 - weston.ini.in | 67 + xwayland/.gitignore | 2 + xwayland/Makefile.am | 42 + xwayland/dnd.c | 274 ++ xwayland/hash.c | 309 ++ xwayland/hash.h | 49 + xwayland/launcher.c | 389 +++ xwayland/selection.c | 712 +++++ xwayland/window-manager.c | 2268 +++++++++++++++ xwayland/xwayland.h | 175 ++ 124 files changed, 22629 insertions(+), 14199 deletions(-) create mode 100644 clients/scaler.c create mode 100644 clients/stacking.c delete mode 100644 clients/tablet-shell.c create mode 100644 desktop-shell/.gitignore create mode 100644 desktop-shell/Makefile.am create mode 100644 desktop-shell/exposay.c create mode 100644 desktop-shell/input-panel.c create mode 100644 desktop-shell/shell.c create mode 100644 desktop-shell/shell.h create mode 100644 protocol/scaler.xml delete mode 100644 protocol/subsurface.xml delete mode 100644 protocol/tablet-shell.xml create mode 100644 protocol/xdg-shell.xml create mode 100644 shared/frame.c create mode 100644 src/dbus.c create mode 100644 src/dbus.h create mode 100644 src/logind-util.c create mode 100644 src/logind-util.h delete mode 100644 src/shell.c delete mode 100644 src/tablet-shell.c delete mode 100644 src/xwayland/Makefile.am delete mode 100644 src/xwayland/dnd.c delete mode 100644 src/xwayland/hash.c delete mode 100644 src/xwayland/hash.h delete mode 100644 src/xwayland/launcher.c delete mode 100644 src/xwayland/selection.c delete mode 100644 src/xwayland/window-manager.c delete mode 100644 src/xwayland/xwayland.h create mode 100644 tests/bad-buffer-test.c create mode 100644 tests/buffer-count-test.c delete mode 100644 weston.ini create mode 100644 weston.ini.in create mode 100644 xwayland/.gitignore create mode 100644 xwayland/Makefile.am create mode 100644 xwayland/dnd.c create mode 100644 xwayland/hash.c create mode 100644 xwayland/hash.h create mode 100644 xwayland/launcher.c create mode 100644 xwayland/selection.c create mode 100644 xwayland/window-manager.c create mode 100644 xwayland/xwayland.h diff --git a/.gitignore b/.gitignore index f96bbd19..111c56c9 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,8 @@ cscope.out /missing /stamp-h1 /test-driver +/weston.ini Makefile Makefile.in TAGS +protocol/.*.valid diff --git a/Makefile.am b/Makefile.am index e9ecc380..f22c5429 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,8 +2,33 @@ if BUILD_WCAP_TOOLS wcap_subdir = wcap endif -SUBDIRS = shared src clients data protocol tests $(wcap_subdir) man +if ENABLE_XWAYLAND +xwayland_subdir = xwayland +endif + +SUBDIRS = \ + shared \ + src \ + $(xwayland_subdir) \ + desktop-shell \ + clients \ + data \ + protocol \ + tests \ + $(wcap_subdir) \ + man DISTCHECK_CONFIGURE_FLAGS = --disable-setuid-install -EXTRA_DIST = weston.ini wayland-scanner.mk +EXTRA_DIST = weston.ini.in wayland-scanner.mk + +weston.ini : $(srcdir)/weston.ini.in + $(AM_V_GEN)$(SED) \ + -e 's|@bindir[@]|$(bindir)|g' \ + -e 's|@abs_top_builddir[@]|$(abs_top_builddir)|g' \ + -e 's|@libexecdir[@]|$(libexecdir)|g' \ + $< > $@ + +all-local : weston.ini + +CLEANFILES = weston.ini diff --git a/clients/.gitignore b/clients/.gitignore index 23959cce..d23027ce 100644 --- a/clients/.gitignore +++ b/clients/.gitignore @@ -11,10 +11,12 @@ weston-image weston-nested weston-nested-client weston-resizor +weston-scaler weston-simple-egl weston-simple-shm weston-simple-touch weston-smoke +weston-stacking weston-subsurfaces weston-transformed weston-view @@ -27,8 +29,8 @@ weston-keyboard libtoytoolkit.a screenshooter-client-protocol.h screenshooter-protocol.c -subsurface-client-protocol.h -subsurface-protocol.c +scaler-client-protocol.h +scaler-protocol.c tablet-shell-client-protocol.h tablet-shell-protocol.c text-client-protocol.h diff --git a/clients/Makefile.am b/clients/Makefile.am index 4f9dc481..4f8d4a6f 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -1,6 +1,4 @@ -bin_PROGRAMS = \ - weston-info \ - $(terminal) +bin_PROGRAMS = $(install_clients) demo_clients = \ $(clients_programs) \ @@ -9,20 +7,12 @@ demo_clients = \ $(simple_clients_programs) \ $(simple_egl_clients_programs) -if ENABLE_DEMO_CLIENTS +if INSTALL_DEMO_CLIENTS bin_PROGRAMS += $(demo_clients) else noinst_PROGRAMS = $(demo_clients) endif -libexec_PROGRAMS = \ - $(desktop_shell) \ - $(tablet_shell) \ - $(screenshooter) \ - $(screensaver) \ - $(keyboard) \ - weston-simple-im - AM_CFLAGS = $(GCC_CFLAGS) AM_CPPFLAGS = \ -DDATADIR='"$(datadir)"' \ @@ -64,7 +54,14 @@ weston_simple_egl_LDADD = $(SIMPLE_EGL_CLIENT_LIBS) -lm endif if BUILD_CLIENTS -terminal = weston-terminal +install_clients = weston-terminal weston-info + +libexec_PROGRAMS = \ + weston-desktop-shell \ + weston-screenshooter \ + $(screensaver) \ + weston-keyboard \ + weston-simple-im clients_programs = \ weston-flower \ @@ -77,19 +74,13 @@ clients_programs = \ weston-clickdot \ weston-transformed \ weston-fullscreen \ + weston-stacking \ weston-calibrator \ + weston-scaler \ $(subsurfaces) \ $(full_gl_client_programs) \ $(cairo_glesv2_programs) -desktop_shell = weston-desktop-shell - -if ENABLE_TABLET_SHELL -tablet_shell = weston-tablet-shell -endif - -screenshooter = weston-screenshooter - noinst_LTLIBRARIES = libtoytoolkit.la libtoytoolkit_la_SOURCES = \ @@ -97,8 +88,8 @@ libtoytoolkit_la_SOURCES = \ window.h \ text-cursor-position-protocol.c \ text-cursor-position-client-protocol.h \ - subsurface-protocol.c \ - subsurface-client-protocol.h \ + scaler-protocol.c \ + scaler-client-protocol.h \ workspaces-protocol.c \ workspaces-client-protocol.h @@ -137,6 +128,9 @@ weston_smoke_LDADD = libtoytoolkit.la weston_resizor_SOURCES = resizor.c weston_resizor_LDADD = libtoytoolkit.la +weston_scaler_SOURCES = scaler.c +weston_scaler_LDADD = libtoytoolkit.la + if HAVE_CAIRO_GLESV2 cairo_glesv2_programs = weston-nested weston-nested-client @@ -159,6 +153,9 @@ weston_transformed_LDADD = libtoytoolkit.la weston_fullscreen_SOURCES = fullscreen.c weston_fullscreen_LDADD = libtoytoolkit.la +weston_stacking_SOURCES = stacking.c +weston_stacking_LDADD = libtoytoolkit.la + weston_calibrator_SOURCES = calibrator.c \ ../shared/matrix.c \ ../shared/matrix.h @@ -181,7 +178,6 @@ weston_editor_LDADD = libtoytoolkit.la $(PANGO_LIBS) weston_editor_CPPFLAGS = $(AM_CPPFLAGS) $(PANGO_CFLAGS) endif -keyboard = weston-keyboard weston_keyboard_SOURCES = \ keyboard.c \ desktop-shell-client-protocol.h \ @@ -208,12 +204,6 @@ weston_desktop_shell_SOURCES = \ desktop-shell-protocol.c weston_desktop_shell_LDADD = libtoytoolkit.la -weston_tablet_shell_SOURCES = \ - tablet-shell.c \ - tablet-shell-client-protocol.h \ - tablet-shell-protocol.c -weston_tablet_shell_LDADD = libtoytoolkit.la - BUILT_SOURCES = \ screenshooter-client-protocol.h \ screenshooter-protocol.c \ @@ -225,10 +215,8 @@ BUILT_SOURCES = \ input-method-client-protocol.h \ desktop-shell-client-protocol.h \ desktop-shell-protocol.c \ - tablet-shell-client-protocol.h \ - tablet-shell-protocol.c \ - subsurface-client-protocol.h \ - subsurface-protocol.c \ + scaler-client-protocol.h \ + scaler-protocol.c \ workspaces-client-protocol.h \ workspaces-protocol.c diff --git a/clients/clickdot.c b/clients/clickdot.c index aebe4db1..c300fa54 100644 --- a/clients/clickdot.c +++ b/clients/clickdot.c @@ -249,7 +249,7 @@ clickdot_create(struct display *display) clickdot = xzalloc(sizeof *clickdot); clickdot->window = window_create(display); - clickdot->widget = frame_create(clickdot->window, clickdot); + clickdot->widget = window_frame_create(clickdot->window, clickdot); window_set_title(clickdot->window, "Wayland ClickDot"); clickdot->display = display; clickdot->buffer = NULL; diff --git a/clients/cliptest.c b/clients/cliptest.c index 4b778087..89c3ca73 100644 --- a/clients/cliptest.c +++ b/clients/cliptest.c @@ -798,7 +798,7 @@ cliptest_create(struct display *display) geometry_init(&cliptest->ui.geometry); cliptest->window = window_create(display); - cliptest->widget = frame_create(cliptest->window, cliptest); + cliptest->widget = window_frame_create(cliptest->window, cliptest); window_set_title(cliptest->window, "cliptest"); cliptest->display = display; diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c index 599c0a5b..4e7a815e 100644 --- a/clients/desktop-shell.c +++ b/clients/desktop-shell.c @@ -95,6 +95,7 @@ struct background { struct output { struct wl_output *output; + uint32_t server_output_id; struct wl_list link; struct panel *panel; @@ -142,7 +143,7 @@ sigchild_handler(int s) } static void -menu_func(struct window *window, int index, void *data) +menu_func(struct window *window, struct input *input, int index, void *data) { printf("Selected index %d from a panel menu.\n", index); } @@ -896,7 +897,7 @@ unlock_dialog_create(struct desktop *desktop) dialog = xzalloc(sizeof *dialog); dialog->window = window_create_custom(display); - dialog->widget = frame_create(dialog->window, dialog); + dialog->widget = window_frame_create(dialog->window, dialog); window_set_title(dialog->window, "Unlock your desktop"); window_set_user_data(dialog->window, dialog); @@ -1213,6 +1214,7 @@ create_output(struct desktop *desktop, uint32_t id) output->output = display_bind(desktop->display, id, &wl_output_interface, 2); + output->server_output_id = id; wl_output_add_listener(output->output, &output_listener, output); @@ -1241,6 +1243,23 @@ global_handler(struct display *display, uint32_t id, } } +static void +global_handler_remove(struct display *display, uint32_t id, + const char *interface, uint32_t version, void *data) +{ + struct desktop *desktop = data; + struct output *output; + + if (!strcmp(interface, "wl_output")) { + wl_list_for_each(output, &desktop->outputs, link) { + if (output->server_output_id == id) { + output_destroy(output); + break; + } + } + } +} + static void panel_add_launchers(struct panel *panel, struct desktop *desktop) { @@ -1298,6 +1317,7 @@ int main(int argc, char *argv[]) display_set_user_data(desktop.display, &desktop); display_set_global_handler(desktop.display, global_handler); + display_set_global_handler_remove(desktop.display, global_handler_remove); /* Create panel and background for outputs processed before the shell * global interface was processed */ diff --git a/clients/dnd.c b/clients/dnd.c index 19cc243c..00739c31 100644 --- a/clients/dnd.c +++ b/clients/dnd.c @@ -334,62 +334,42 @@ static const struct wl_data_source_listener data_source_listener = { }; static cairo_surface_t * -create_drag_cursor(struct dnd_drag *dnd_drag, - struct item *item, int32_t x, int32_t y, double opacity) +create_drag_icon(struct dnd_drag *dnd_drag, + struct item *item, int32_t x, int32_t y, double opacity) { struct dnd *dnd = dnd_drag->dnd; cairo_surface_t *surface; - struct wl_cursor_image *pointer; struct rectangle rectangle; cairo_pattern_t *pattern; cairo_t *cr; - pointer = display_get_pointer_image(dnd->display, CURSOR_DRAGGING); - if (!pointer) { - fprintf(stderr, "WARNING: grabbing cursor image not found\n"); - pointer = display_get_pointer_image(dnd->display, - CURSOR_LEFT_PTR); - assert(pointer && "no cursor image found"); - } - - rectangle.width = item_width + 2 * pointer->width; - rectangle.height = item_height + 2 * pointer->height; + rectangle.width = item_width; + rectangle.height = item_height; surface = display_create_surface(dnd->display, NULL, &rectangle, SURFACE_SHM); cr = cairo_create(surface); - cairo_translate(cr, pointer->width, pointer->height); - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba(cr, 0, 0, 0, 0); - cairo_paint(cr); - - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source_surface(cr, item->surface, 0, 0); pattern = cairo_pattern_create_rgba(0, 0, 0, opacity); cairo_mask(cr, pattern); cairo_pattern_destroy(pattern); - /* FIXME: more cairo-gl brokeness */ - surface_flush_device(surface); cairo_destroy(cr); - dnd_drag->hotspot_x = pointer->width + x - item->x; - dnd_drag->hotspot_y = pointer->height + y - item->y; + dnd_drag->hotspot_x = x - item->x; + dnd_drag->hotspot_y = y - item->y; dnd_drag->width = rectangle.width; dnd_drag->height = rectangle.height; return surface; } -static void -dnd_button_handler(struct widget *widget, - struct input *input, uint32_t time, - uint32_t button, enum wl_pointer_button_state state, - void *data) +static int +create_drag_source(struct dnd *dnd, + struct input *input, uint32_t time, + int32_t x, int32_t y) { - struct dnd *dnd = data; - int32_t x, y; struct item *item; struct rectangle allocation; struct dnd_drag *dnd_drag; @@ -401,12 +381,11 @@ dnd_button_handler(struct widget *widget, cairo_surface_t *icon; widget_get_allocation(dnd->widget, &allocation); - input_get_position(input, &x, &y); item = dnd_get_item(dnd, x, y); x -= allocation.x; y -= allocation.y; - if (item && state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (item) { dnd_drag = xmalloc(sizeof *dnd_drag); dnd_drag->dnd = dnd; dnd_drag->input = input; @@ -428,8 +407,6 @@ dnd_button_handler(struct widget *widget, dnd_drag->drag_surface = wl_compositor_create_surface(compositor); - input_ungrab(input); - if (dnd->self_only) { dnd_drag->data_source = NULL; } else { @@ -450,12 +427,10 @@ dnd_button_handler(struct widget *widget, dnd_drag->drag_surface, serial); - input_set_pointer_image(input, CURSOR_DRAGGING); - dnd_drag->opaque = - create_drag_cursor(dnd_drag, item, x, y, 1); + create_drag_icon(dnd_drag, item, x, y, 1); dnd_drag->translucent = - create_drag_cursor(dnd_drag, item, x, y, 0.2); + create_drag_icon(dnd_drag, item, x, y, 0.2); if (dnd->self_only) icon = dnd_drag->opaque; @@ -471,9 +446,47 @@ dnd_button_handler(struct widget *widget, dnd->current_drag = dnd_drag; window_schedule_redraw(dnd->window); + + return 0; + } else + return -1; +} + +static void +dnd_button_handler(struct widget *widget, + struct input *input, uint32_t time, + uint32_t button, enum wl_pointer_button_state state, + void *data) +{ + struct dnd *dnd = data; + int32_t x, y; + + input_get_position(input, &x, &y); + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + input_ungrab(input); + if (create_drag_source(dnd, input, time, x, y) == 0) + input_set_pointer_image(input, CURSOR_DRAGGING); } } +static void +dnd_touch_down_handler(struct widget *widget, + struct input *input, uint32_t serial, + uint32_t time, int32_t id, + float x, float y, void *data) +{ + struct dnd *dnd = data; + int32_t int_x, int_y; + + if (id > 0) + return; + + int_x = (int32_t)x; + int_y = (int32_t)y; + if (create_drag_source(dnd, input, time, int_x, int_y) == 0) + touch_grab(input, 0); +} + static int lookup_cursor(struct dnd *dnd, int x, int y) { @@ -589,7 +602,7 @@ dnd_create(struct display *display) dnd = xzalloc(sizeof *dnd); dnd->window = window_create(display); - dnd->widget = frame_create(dnd->window, dnd); + dnd->widget = window_frame_create(dnd->window, dnd); window_set_title(dnd->window, "Wayland Drag and Drop Demo"); dnd->display = display; @@ -614,11 +627,12 @@ dnd_create(struct display *display) widget_set_enter_handler(dnd->widget, dnd_enter_handler); widget_set_motion_handler(dnd->widget, dnd_motion_handler); widget_set_button_handler(dnd->widget, dnd_button_handler); + widget_set_touch_down_handler(dnd->widget, dnd_touch_down_handler); width = 4 * (item_width + item_padding) + item_padding; height = 4 * (item_height + item_padding) + item_padding; - frame_set_child_size(dnd->widget, width, height); + window_frame_set_child_size(dnd->widget, width, height); return dnd; } diff --git a/clients/editor.c b/clients/editor.c index 12650f32..76e2346a 100644 --- a/clients/editor.c +++ b/clients/editor.c @@ -115,6 +115,9 @@ static void text_entry_button_handler(struct widget *widget, struct input *input, uint32_t time, uint32_t button, enum wl_pointer_button_state state, void *data); +static void text_entry_touch_handler(struct widget *widget, struct input *input, + uint32_t serial, uint32_t time, int32_t id, + float tx, float ty, void *data); static int text_entry_motion_handler(struct widget *widget, struct input *input, uint32_t time, float x, float y, void *data); @@ -516,6 +519,7 @@ text_entry_create(struct editor *editor, const char *text) widget_set_redraw_handler(entry->widget, text_entry_redraw_handler); widget_set_button_handler(entry->widget, text_entry_button_handler); widget_set_motion_handler(entry->widget, text_entry_motion_handler); + widget_set_touch_down_handler(entry->widget, text_entry_touch_handler); return entry; } @@ -1077,6 +1081,29 @@ text_entry_button_handler(struct widget *widget, } } +static void +text_entry_touch_handler(struct widget *widget, struct input *input, + uint32_t serial, uint32_t time, int32_t id, + float tx, float ty, void *data) +{ + struct text_entry *entry = data; + struct wl_seat *seat = input_get_seat(input); + struct rectangle allocation; + struct editor *editor; + int32_t x, y; + + widget_get_allocation(entry->widget, &allocation); + + x = tx - (allocation.x + text_offset_left); + y = ty - (allocation.y + text_offset_left); + + editor = window_get_user_data(entry->window); + text_entry_activate(entry, seat); + editor->active_entry = entry; + + text_entry_set_cursor_position(entry, x, y, true); +} + static void editor_button_handler(struct widget *widget, struct input *input, uint32_t time, @@ -1098,6 +1125,29 @@ editor_button_handler(struct widget *widget, } } +static void +editor_touch_handler(struct widget *widget, struct input *input, + uint32_t serial, uint32_t time, int32_t id, + float tx, float ty, void *data) +{ + struct editor *editor = data; + + struct wl_seat *seat = input_get_seat(input); + + text_entry_deactivate(editor->entry, seat); + text_entry_deactivate(editor->editor, seat); + editor->active_entry = NULL; +} + +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data) +{ + struct editor *editor = data; + + window_schedule_redraw(editor->window); +} + static void key_handler(struct window *window, struct input *input, uint32_t time, @@ -1221,7 +1271,7 @@ main(int argc, char *argv[]) display_set_global_handler(editor.display, global_handler); editor.window = window_create(editor.display); - editor.widget = frame_create(editor.window, &editor); + editor.widget = window_frame_create(editor.window, &editor); editor.entry = text_entry_create(&editor, "Entry"); editor.entry->click_to_show = click_to_show; @@ -1233,11 +1283,14 @@ main(int argc, char *argv[]) window_set_title(editor.window, "Text Editor"); window_set_key_handler(editor.window, key_handler); + window_set_keyboard_focus_handler(editor.window, + keyboard_focus_handler); window_set_user_data(editor.window, &editor); widget_set_redraw_handler(editor.widget, redraw_handler); widget_set_resize_handler(editor.widget, resize_handler); widget_set_button_handler(editor.widget, editor_button_handler); + widget_set_touch_down_handler(editor.widget, editor_touch_handler); window_schedule_resize(editor.window, 500, 400); diff --git a/clients/eventdemo.c b/clients/eventdemo.c index 05ad5dcb..029b1d8b 100644 --- a/clients/eventdemo.c +++ b/clients/eventdemo.c @@ -307,7 +307,7 @@ eventdemo_create(struct display *d) */ e->widget = window_add_widget(e->window, e); } else { - e->widget = frame_create(e->window, e); + e->widget = window_frame_create(e->window, e); window_set_title(e->window, title); } e->display = d; diff --git a/clients/flower.c b/clients/flower.c index 825c833e..389f8bee 100644 --- a/clients/flower.c +++ b/clients/flower.c @@ -158,8 +158,7 @@ touch_down_handler(struct widget *widget, struct input *input, float x, float y, void *data) { struct flower *flower = data; - window_touch_move(flower->window, input, - display_get_serial(flower->display)); + window_move(flower->window, input, display_get_serial(flower->display)); } int main(int argc, char *argv[]) diff --git a/clients/fullscreen.c b/clients/fullscreen.c index 72e2c81f..034a3528 100644 --- a/clients/fullscreen.c +++ b/clients/fullscreen.c @@ -283,8 +283,7 @@ touch_handler(struct widget *widget, struct input *input, float x, float y, void *data) { struct fullscreen *fullscreen = data; - window_touch_move(fullscreen->window, input, - display_get_serial(fullscreen->display)); + window_move(fullscreen->window, input, display_get_serial(fullscreen->display)); } static void diff --git a/clients/gears.c b/clients/gears.c index 30f4e688..93a86b4f 100644 --- a/clients/gears.c +++ b/clients/gears.c @@ -404,7 +404,7 @@ gears_create(struct display *display) gears = zalloc(sizeof *gears); gears->d = display; gears->window = window_create(display); - gears->widget = frame_create(gears->window, gears); + gears->widget = window_frame_create(gears->window, gears); window_set_title(gears->window, "Wayland Gears"); gears->display = display_get_egl_display(gears->d); diff --git a/clients/image.c b/clients/image.c index 4d3f3b87..3a52c220 100644 --- a/clients/image.c +++ b/clients/image.c @@ -336,7 +336,7 @@ fullscreen_handler(struct window *window, void *data) } static void -close_handler(struct window *window, void *data) +close_handler(void *data) { struct image *image = data; @@ -378,7 +378,7 @@ image_create(struct display *display, const char *filename, } image->window = window_create(display); - image->widget = frame_create(image->window, image); + image->widget = window_frame_create(image->window, image); window_set_title(image->window, title); image->display = display; image->image_counter = image_counter; diff --git a/clients/keyboard.c b/clients/keyboard.c index 9ee4a848..6876cdec 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -261,6 +261,19 @@ struct keyboard { enum keyboard_state state; }; +static void __attribute__ ((format (printf, 1, 2))) +dbg(const char *fmt, ...) +{ +#ifdef DEBUG + int l; + va_list argp; + + va_start(argp, fmt); + l = vfprintf(stderr, fmt, argp); + va_end(argp); +#endif +} + static const char * label_from_key(struct keyboard *keyboard, const struct key *key) @@ -384,12 +397,13 @@ resize_handler(struct widget *widget, static char * insert_text(const char *text, uint32_t offset, const char *insert) { - char *new_text = xmalloc(strlen(text) + strlen(insert) + 1); + int tlen = strlen(text), ilen = strlen(insert); + char *new_text = xmalloc(tlen + ilen + 1); - strncat(new_text, text, offset); - new_text[offset] = '\0'; - strcat(new_text, insert); - strcat(new_text, text + offset); + memcpy(new_text, text, offset); + memcpy(new_text + offset, insert, ilen); + memcpy(new_text + offset + ilen, text + offset, tlen - offset); + new_text[tlen + ilen] = '\0'; return new_text; } @@ -462,14 +476,14 @@ delete_before_cursor(struct virtual_keyboard *keyboard) const char *start, *end; if (!keyboard->surrounding_text) { - fprintf(stderr, "delete_before_cursor: No surrounding text available\n"); + dbg("delete_before_cursor: No surrounding text available\n"); return; } start = prev_utf8_char(keyboard->surrounding_text, keyboard->surrounding_text + keyboard->surrounding_cursor); if (!start) { - fprintf(stderr, "delete_before_cursor: No previous character to delete\n"); + dbg("delete_before_cursor: No previous character to delete\n"); return; } @@ -489,6 +503,21 @@ delete_before_cursor(struct virtual_keyboard *keyboard) memmove(keyboard->surrounding_text + keyboard->surrounding_cursor, end, strlen(end)); } +static char * +append(char *s1, const char *s2) +{ + int len1, len2; + char *s; + + len1 = strlen(s1); + len2 = strlen(s2); + s = xrealloc(s1, len1 + len2 + 1); + memcpy(s + len1, s2, len2); + s[len1 + len2] = '\0'; + + return s; +} + static void keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key *key, struct input *input, enum wl_pointer_button_state state) { @@ -501,8 +530,9 @@ keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key * if (state != WL_POINTER_BUTTON_STATE_PRESSED) break; - keyboard->keyboard->preedit_string = strcat(keyboard->keyboard->preedit_string, - label); + keyboard->keyboard->preedit_string = + append(keyboard->keyboard->preedit_string, + label); virtual_keyboard_send_preedit(keyboard->keyboard, -1); break; case keytype_backspace: @@ -526,8 +556,8 @@ keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key * case keytype_space: if (state != WL_POINTER_BUTTON_STATE_PRESSED) break; - keyboard->keyboard->preedit_string = strcat(keyboard->keyboard->preedit_string, - " "); + keyboard->keyboard->preedit_string = + append(keyboard->keyboard->preedit_string, " "); virtual_keyboard_commit_preedit(keyboard->keyboard); break; case keytype_switch: @@ -625,11 +655,9 @@ button_handler(struct widget *widget, } static void -touch_down_handler(struct widget *widget, struct input *input, - uint32_t serial, uint32_t time, int32_t id, - float x, float y, void *data) +touch_handler(struct input *input, uint32_t time, + float x, float y, uint32_t state, void *data) { - struct keyboard *keyboard = data; struct rectangle allocation; int row, col; @@ -648,20 +676,35 @@ touch_down_handler(struct widget *widget, struct input *input, for (i = 0; i < layout->count; ++i) { col -= layout->keys[i].width; if (col < 0) { - keyboard_handle_key(keyboard, time, &layout->keys[i], input, WL_POINTER_BUTTON_STATE_PRESSED); + keyboard_handle_key(keyboard, time, + &layout->keys[i], input, state); break; } } - widget_schedule_redraw(widget); + widget_schedule_redraw(keyboard->widget); +} + +static void +touch_down_handler(struct widget *widget, struct input *input, + uint32_t serial, uint32_t time, int32_t id, + float x, float y, void *data) +{ + touch_handler(input, time, x, y, + WL_POINTER_BUTTON_STATE_PRESSED, data); } static void touch_up_handler(struct widget *widget, struct input *input, - uint32_t serial, uint32_t time, int32_t id, - void *data) + uint32_t serial, uint32_t time, int32_t id, + void *data) { + float x, y; + input_get_touch(input, id, &x, &y); + + touch_handler(input, time, x, y, + WL_POINTER_BUTTON_STATE_RELEASED, data); } static void @@ -685,7 +728,7 @@ handle_reset(void *data, { struct virtual_keyboard *keyboard = data; - fprintf(stderr, "Reset pre-edit buffer\n"); + dbg("Reset pre-edit buffer\n"); if (strlen(keyboard->preedit_string)) { free(keyboard->preedit_string); @@ -732,7 +775,7 @@ handle_commit_state(void *data, layout = get_current_layout(keyboard); if (keyboard->surrounding_text) - fprintf(stderr, "Surrounding text updated: %s\n", keyboard->surrounding_text); + dbg("Surrounding text updated: %s\n", keyboard->surrounding_text); window_schedule_resize(keyboard->keyboard->window, layout->columns * key_width, @@ -882,7 +925,6 @@ keyboard_create(struct output *output, struct virtual_keyboard *virtual_keyboard widget_set_touch_down_handler(keyboard->widget, touch_down_handler); widget_set_touch_up_handler(keyboard->widget, touch_up_handler); - window_schedule_resize(keyboard->window, layout->columns * key_width, layout->rows * key_height); diff --git a/clients/nested.c b/clients/nested.c index 50852340..74767795 100644 --- a/clients/nested.c +++ b/clients/nested.c @@ -47,6 +47,18 @@ #define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#ifndef EGL_WL_create_wayland_buffer_from_image +#define EGL_WL_create_wayland_buffer_from_image 1 + +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI struct wl_buffer * EGLAPIENTRY eglCreateWaylandBufferFromImageWL(EGLDisplay dpy, EGLImageKHR image); +#endif +typedef struct wl_buffer * (EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL) (EGLDisplay dpy, EGLImageKHR image); + +#endif + +static int option_blit; + struct nested { struct display *display; struct window *window; @@ -58,7 +70,8 @@ struct nested { struct program *texture_program; struct wl_list surface_list; - struct wl_list frame_callback_list; + + const struct nested_renderer *renderer; }; struct nested_region { @@ -66,52 +79,194 @@ struct nested_region { pixman_region32_t region; }; +struct nested_buffer_reference { + struct nested_buffer *buffer; + struct wl_listener destroy_listener; +}; + +struct nested_buffer { + struct wl_resource *resource; + struct wl_signal destroy_signal; + struct wl_listener destroy_listener; + uint32_t busy_count; + + /* A buffer in the parent compositor representing the same + * data. This is created on-demand when the subsurface + * renderer is used */ + struct wl_buffer *parent_buffer; + /* This reference is used to mark when the parent buffer has + * been attached to the subsurface. It will be unrefenced when + * we receive a buffer release event. That way we won't inform + * the client that the buffer is free until the parent + * compositor is also finished with it */ + struct nested_buffer_reference parent_ref; +}; + struct nested_surface { struct wl_resource *resource; - struct wl_resource *buffer_resource; struct nested *nested; EGLImageKHR *image; - GLuint texture; struct wl_list link; + + struct wl_list frame_callback_list; + + struct { + /* wl_surface.attach */ + int newly_attached; + struct nested_buffer *buffer; + struct wl_listener buffer_destroy_listener; + + /* wl_surface.frame */ + struct wl_list frame_callback_list; + + /* wl_surface.damage */ + pixman_region32_t damage; + } pending; + + void *renderer_data; +}; + +/* Data used for the blit renderer */ +struct nested_blit_surface { + struct nested_buffer_reference buffer_ref; + GLuint texture; cairo_surface_t *cairo_surface; }; +/* Data used for the subsurface renderer */ +struct nested_ss_surface { + struct widget *widget; + struct wl_surface *surface; + struct wl_subsurface *subsurface; + struct wl_callback *frame_callback; +}; + struct nested_frame_callback { struct wl_resource *resource; struct wl_list link; }; +struct nested_renderer { + void (* surface_init)(struct nested_surface *surface); + void (* surface_fini)(struct nested_surface *surface); + void (* render_clients)(struct nested *nested, cairo_t *cr); + void (* surface_attach)(struct nested_surface *surface, + struct nested_buffer *buffer); +}; + +static const struct weston_option nested_options[] = { + { WESTON_OPTION_BOOLEAN, "blit", 'b', &option_blit }, +}; + +static const struct nested_renderer nested_blit_renderer; +static const struct nested_renderer nested_ss_renderer; + static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d; static PFNEGLCREATEIMAGEKHRPROC create_image; static PFNEGLDESTROYIMAGEKHRPROC destroy_image; static PFNEGLBINDWAYLANDDISPLAYWL bind_display; static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display; static PFNEGLQUERYWAYLANDBUFFERWL query_buffer; +static PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL create_wayland_buffer_from_image; static void -frame_callback(void *data, struct wl_callback *callback, uint32_t time) +nested_buffer_destroy_handler(struct wl_listener *listener, void *data) { - struct nested *nested = data; - struct nested_frame_callback *nc, *next; + struct nested_buffer *buffer = + container_of(listener, struct nested_buffer, destroy_listener); - if (callback) - wl_callback_destroy(callback); + wl_signal_emit(&buffer->destroy_signal, buffer); + + if (buffer->parent_buffer) + wl_buffer_destroy(buffer->parent_buffer); + + free(buffer); +} + +static struct nested_buffer * +nested_buffer_from_resource(struct wl_resource *resource) +{ + struct nested_buffer *buffer; + struct wl_listener *listener; + + listener = + wl_resource_get_destroy_listener(resource, + nested_buffer_destroy_handler); + + if (listener) + return container_of(listener, struct nested_buffer, + destroy_listener); + + buffer = zalloc(sizeof *buffer); + if (buffer == NULL) + return NULL; - wl_list_for_each_safe(nc, next, &nested->frame_callback_list, link) { + buffer->resource = resource; + wl_signal_init(&buffer->destroy_signal); + buffer->destroy_listener.notify = nested_buffer_destroy_handler; + wl_resource_add_destroy_listener(resource, &buffer->destroy_listener); + + return buffer; +} + +static void +nested_buffer_reference_handle_destroy(struct wl_listener *listener, + void *data) +{ + struct nested_buffer_reference *ref = + container_of(listener, struct nested_buffer_reference, + destroy_listener); + + assert((struct nested_buffer *)data == ref->buffer); + ref->buffer = NULL; +} + +static void +nested_buffer_reference(struct nested_buffer_reference *ref, + struct nested_buffer *buffer) +{ + if (buffer == ref->buffer) + return; + + if (ref->buffer) { + ref->buffer->busy_count--; + if (ref->buffer->busy_count == 0) { + assert(wl_resource_get_client(ref->buffer->resource)); + wl_resource_queue_event(ref->buffer->resource, + WL_BUFFER_RELEASE); + } + wl_list_remove(&ref->destroy_listener.link); + } + + if (buffer) { + buffer->busy_count++; + wl_signal_add(&buffer->destroy_signal, + &ref->destroy_listener); + + ref->destroy_listener.notify = + nested_buffer_reference_handle_destroy; + } + + ref->buffer = buffer; +} + +static void +flush_surface_frame_callback_list(struct nested_surface *surface, + uint32_t time) +{ + struct nested_frame_callback *nc, *next; + + wl_list_for_each_safe(nc, next, &surface->frame_callback_list, link) { wl_callback_send_done(nc->resource, time); wl_resource_destroy(nc->resource); } - wl_list_init(&nested->frame_callback_list); + wl_list_init(&surface->frame_callback_list); /* FIXME: toytoolkit need a pre-block handler where we can * call this. */ - wl_display_flush_clients(nested->child_display); + wl_display_flush_clients(surface->nested->child_display); } -static const struct wl_callback_listener frame_listener = { - frame_callback -}; - static void redraw_handler(struct widget *widget, void *data) { @@ -119,8 +274,6 @@ redraw_handler(struct widget *widget, void *data) cairo_surface_t *surface; cairo_t *cr; struct rectangle allocation; - struct wl_callback *callback; - struct nested_surface *s; widget_get_allocation(nested->widget, &allocation); @@ -136,34 +289,11 @@ redraw_handler(struct widget *widget, void *data) cairo_set_source_rgba(cr, 0, 0, 0, 0.8); cairo_fill(cr); - wl_list_for_each(s, &nested->surface_list, link) { - display_acquire_window_surface(nested->display, - nested->window, NULL); - - glBindTexture(GL_TEXTURE_2D, s->texture); - image_target_texture_2d(GL_TEXTURE_2D, s->image); - - display_release_window_surface(nested->display, - nested->window); - - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_set_source_surface(cr, s->cairo_surface, - allocation.x + 10, - allocation.y + 10); - cairo_rectangle(cr, allocation.x + 10, - allocation.y + 10, - allocation.width - 10, - allocation.height - 10); - - cairo_fill(cr); - } + nested->renderer->render_clients(nested, cr); cairo_destroy(cr); cairo_surface_destroy(surface); - - callback = wl_surface_frame(window_get_wl_surface(nested->window)); - wl_callback_add_listener(callback, &frame_listener, nested); } static void @@ -264,6 +394,22 @@ static void destroy_surface(struct wl_resource *resource) { struct nested_surface *surface = wl_resource_get_user_data(resource); + struct nested *nested = surface->nested; + struct nested_frame_callback *cb, *next; + + wl_list_for_each_safe(cb, next, + &surface->frame_callback_list, link) + wl_resource_destroy(cb->resource); + + wl_list_for_each_safe(cb, next, + &surface->pending.frame_callback_list, link) + wl_resource_destroy(cb->resource); + + pixman_region32_fini(&surface->pending.damage); + + nested->renderer->surface_fini(surface); + + wl_list_remove(&surface->link); free(surface); } @@ -281,54 +427,66 @@ surface_attach(struct wl_client *client, { struct nested_surface *surface = wl_resource_get_user_data(resource); struct nested *nested = surface->nested; - EGLint format, width, height; - cairo_device_t *device; + struct nested_buffer *buffer = NULL; - if (surface->buffer_resource) - wl_buffer_send_release(surface->buffer_resource); + if (buffer_resource) { + int format; - surface->buffer_resource = buffer_resource; - if (!query_buffer(nested->egl_display, (void *) buffer_resource, - EGL_TEXTURE_FORMAT, &format)) { - fprintf(stderr, "attaching non-egl wl_buffer\n"); - return; + if (!query_buffer(nested->egl_display, (void *) buffer_resource, + EGL_TEXTURE_FORMAT, &format)) { + wl_resource_post_error(buffer_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "attaching non-egl wl_buffer"); + return; + } + + switch (format) { + case EGL_TEXTURE_RGB: + case EGL_TEXTURE_RGBA: + break; + default: + wl_resource_post_error(buffer_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "invalid format"); + return; + } + + buffer = nested_buffer_from_resource(buffer_resource); + if (buffer == NULL) { + wl_client_post_no_memory(client); + return; + } } + if (surface->pending.buffer) + wl_list_remove(&surface->pending.buffer_destroy_listener.link); + + surface->pending.buffer = buffer; + surface->pending.newly_attached = 1; + if (buffer) { + wl_signal_add(&buffer->destroy_signal, + &surface->pending.buffer_destroy_listener); + } +} + +static void +nested_surface_attach(struct nested_surface *surface, + struct nested_buffer *buffer) +{ + struct nested *nested = surface->nested; + if (surface->image != EGL_NO_IMAGE_KHR) destroy_image(nested->egl_display, surface->image); - if (surface->cairo_surface) - cairo_surface_destroy(surface->cairo_surface); - - switch (format) { - case EGL_TEXTURE_RGB: - case EGL_TEXTURE_RGBA: - break; - default: - fprintf(stderr, "unhandled format: %x\n", format); - return; - } surface->image = create_image(nested->egl_display, NULL, - EGL_WAYLAND_BUFFER_WL, buffer_resource, + EGL_WAYLAND_BUFFER_WL, buffer->resource, NULL); if (surface->image == EGL_NO_IMAGE_KHR) { fprintf(stderr, "failed to create img\n"); return; } - query_buffer(nested->egl_display, - (void *) buffer_resource, EGL_WIDTH, &width); - query_buffer(nested->egl_display, - (void *) buffer_resource, EGL_HEIGHT, &height); - - device = display_get_cairo_device(nested->display); - surface->cairo_surface = - cairo_gl_surface_create_for_texture(device, - CAIRO_CONTENT_COLOR_ALPHA, - surface->texture, - width, height); - - window_schedule_redraw(nested->window); + nested->renderer->surface_attach(surface, buffer); } static void @@ -336,6 +494,11 @@ surface_damage(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { + struct nested_surface *surface = wl_resource_get_user_data(resource); + + pixman_region32_union_rect(&surface->pending.damage, + &surface->pending.damage, + x, y, width, height); } static void @@ -353,7 +516,6 @@ surface_frame(struct wl_client *client, { struct nested_frame_callback *callback; struct nested_surface *surface = wl_resource_get_user_data(resource); - struct nested *nested = surface->nested; callback = malloc(sizeof *callback); if (callback == NULL) { @@ -366,7 +528,8 @@ surface_frame(struct wl_client *client, wl_resource_set_implementation(callback->resource, NULL, callback, destroy_frame_callback); - wl_list_insert(nested->frame_callback_list.prev, &callback->link); + wl_list_insert(surface->pending.frame_callback_list.prev, + &callback->link); } static void @@ -385,9 +548,42 @@ surface_set_input_region(struct wl_client *client, fprintf(stderr, "surface_set_input_region\n"); } +static void +empty_region(pixman_region32_t *region) +{ + pixman_region32_fini(region); + pixman_region32_init(region); +} + static void surface_commit(struct wl_client *client, struct wl_resource *resource) { + struct nested_surface *surface = wl_resource_get_user_data(resource); + struct nested *nested = surface->nested; + + /* wl_surface.attach */ + if (surface->pending.newly_attached) + nested_surface_attach(surface, surface->pending.buffer); + + if (surface->pending.buffer) { + wl_list_remove(&surface->pending.buffer_destroy_listener.link); + surface->pending.buffer = NULL; + } + surface->pending.newly_attached = 0; + + /* wl_surface.damage */ + empty_region(&surface->pending.damage); + + /* wl_surface.frame */ + wl_list_insert_list(&surface->frame_callback_list, + &surface->pending.frame_callback_list); + wl_list_init(&surface->pending.frame_callback_list); + + /* FIXME: For the subsurface renderer we don't need to + * actually redraw the window. However we do want to cause a + * commit because the subsurface is synchronized. Ideally we + * would just queue the commit */ + window_schedule_redraw(nested->window); } static void @@ -408,6 +604,16 @@ static const struct wl_surface_interface surface_interface = { surface_set_buffer_transform }; +static void +surface_handle_pending_buffer_destroy(struct wl_listener *listener, void *data) +{ + struct nested_surface *surface = + container_of(listener, struct nested_surface, + pending.buffer_destroy_listener); + + surface->pending.buffer = NULL; +} + static void compositor_create_surface(struct wl_client *client, struct wl_resource *resource, uint32_t id) @@ -423,15 +629,17 @@ compositor_create_surface(struct wl_client *client, surface->nested = nested; + wl_list_init(&surface->frame_callback_list); + + wl_list_init(&surface->pending.frame_callback_list); + surface->pending.buffer_destroy_listener.notify = + surface_handle_pending_buffer_destroy; + pixman_region32_init(&surface->pending.damage); + display_acquire_window_surface(nested->display, nested->window, NULL); - glGenTextures(1, &surface->texture); - glBindTexture(GL_TEXTURE_2D, surface->texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + nested->renderer->surface_init(surface); display_release_window_surface(nested->display, nested->window); @@ -531,10 +739,10 @@ nested_init_compositor(struct nested *nested) { const char *extensions; struct wl_event_loop *loop; + int use_ss_renderer = 0; int fd, ret; wl_list_init(&nested->surface_list); - wl_list_init(&nested->frame_callback_list); nested->child_display = wl_display_create(); loop = wl_display_get_event_loop(nested->child_display); fd = wl_event_loop_get_fd(loop); @@ -570,6 +778,29 @@ nested_init_compositor(struct nested *nested) return -1; } + if (display_has_subcompositor(nested->display)) { + const char *func = "eglCreateWaylandBufferFromImageWL"; + const char *ext = "EGL_WL_create_wayland_buffer_from_image"; + + if (strstr(extensions, ext)) { + create_wayland_buffer_from_image = + (void *) eglGetProcAddress(func); + use_ss_renderer = 1; + } + } + + if (option_blit) + use_ss_renderer = 0; + + if (use_ss_renderer) { + printf("Using subsurfaces to render client surfaces\n"); + nested->renderer = &nested_ss_renderer; + } else { + printf("Using local compositing with blits to " + "render client surfaces\n"); + nested->renderer = &nested_blit_renderer; + } + return 0; } @@ -583,7 +814,7 @@ nested_create(struct display *display) return nested; nested->window = window_create(display); - nested->widget = frame_create(nested->window, nested); + nested->widget = window_frame_create(nested->window, nested); window_set_title(nested->window, "Wayland Nested"); nested->display = display; @@ -607,12 +838,289 @@ nested_destroy(struct nested *nested) free(nested); } +/*** blit renderer ***/ + +static void +blit_surface_init(struct nested_surface *surface) +{ + struct nested_blit_surface *blit_surface = + zalloc(sizeof *blit_surface); + + glGenTextures(1, &blit_surface->texture); + glBindTexture(GL_TEXTURE_2D, blit_surface->texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + surface->renderer_data = blit_surface; +} + +static void +blit_surface_fini(struct nested_surface *surface) +{ + struct nested_blit_surface *blit_surface = surface->renderer_data; + + nested_buffer_reference(&blit_surface->buffer_ref, NULL); + + glDeleteTextures(1, &blit_surface->texture); + + free(blit_surface); +} + +static void +blit_frame_callback(void *data, struct wl_callback *callback, uint32_t time) +{ + struct nested *nested = data; + struct nested_surface *surface; + + wl_list_for_each(surface, &nested->surface_list, link) + flush_surface_frame_callback_list(surface, time); + + if (callback) + wl_callback_destroy(callback); +} + +static const struct wl_callback_listener blit_frame_listener = { + blit_frame_callback +}; + +static void +blit_render_clients(struct nested *nested, + cairo_t *cr) +{ + struct nested_surface *s; + struct rectangle allocation; + struct wl_callback *callback; + + widget_get_allocation(nested->widget, &allocation); + + wl_list_for_each(s, &nested->surface_list, link) { + struct nested_blit_surface *blit_surface = s->renderer_data; + + display_acquire_window_surface(nested->display, + nested->window, NULL); + + glBindTexture(GL_TEXTURE_2D, blit_surface->texture); + image_target_texture_2d(GL_TEXTURE_2D, s->image); + + display_release_window_surface(nested->display, + nested->window); + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(cr, blit_surface->cairo_surface, + allocation.x + 10, + allocation.y + 10); + cairo_rectangle(cr, allocation.x + 10, + allocation.y + 10, + allocation.width - 10, + allocation.height - 10); + + cairo_fill(cr); + } + + callback = wl_surface_frame(window_get_wl_surface(nested->window)); + wl_callback_add_listener(callback, &blit_frame_listener, nested); +} + +static void +blit_surface_attach(struct nested_surface *surface, + struct nested_buffer *buffer) +{ + struct nested *nested = surface->nested; + struct nested_blit_surface *blit_surface = surface->renderer_data; + EGLint width, height; + cairo_device_t *device; + + nested_buffer_reference(&blit_surface->buffer_ref, buffer); + + if (blit_surface->cairo_surface) + cairo_surface_destroy(blit_surface->cairo_surface); + + query_buffer(nested->egl_display, (void *) buffer->resource, + EGL_WIDTH, &width); + query_buffer(nested->egl_display, (void *) buffer->resource, + EGL_HEIGHT, &height); + + device = display_get_cairo_device(nested->display); + blit_surface->cairo_surface = + cairo_gl_surface_create_for_texture(device, + CAIRO_CONTENT_COLOR_ALPHA, + blit_surface->texture, + width, height); +} + +static const struct nested_renderer +nested_blit_renderer = { + .surface_init = blit_surface_init, + .surface_fini = blit_surface_fini, + .render_clients = blit_render_clients, + .surface_attach = blit_surface_attach +}; + +/*** subsurface renderer ***/ + +static void +ss_surface_init(struct nested_surface *surface) +{ + struct nested *nested = surface->nested; + struct wl_compositor *compositor = + display_get_compositor(nested->display); + struct nested_ss_surface *ss_surface = + zalloc(sizeof *ss_surface); + struct rectangle allocation; + struct wl_region *region; + + ss_surface->widget = + window_add_subsurface(nested->window, + nested, + SUBSURFACE_SYNCHRONIZED); + + ss_surface->surface = widget_get_wl_surface(ss_surface->widget); + ss_surface->subsurface = widget_get_wl_subsurface(ss_surface->widget); + + /* The toy toolkit gets confused about the pointer position + * when it gets motion events for a subsurface so we'll just + * disable input on it */ + region = wl_compositor_create_region(compositor); + wl_surface_set_input_region(ss_surface->surface, region); + wl_region_destroy(region); + + widget_get_allocation(nested->widget, &allocation); + wl_subsurface_set_position(ss_surface->subsurface, + allocation.x + 10, + allocation.y + 10); + + surface->renderer_data = ss_surface; +} + +static void +ss_surface_fini(struct nested_surface *surface) +{ + struct nested_ss_surface *ss_surface = surface->renderer_data; + + widget_destroy(ss_surface->widget); + + if (ss_surface->frame_callback) + wl_callback_destroy(ss_surface->frame_callback); + + free(ss_surface); +} + +static void +ss_render_clients(struct nested *nested, + cairo_t *cr) +{ + /* The clients are composited by the parent compositor so we + * don't need to do anything here */ +} + +static void +ss_buffer_release(void *data, struct wl_buffer *wl_buffer) +{ + struct nested_buffer *buffer = data; + + nested_buffer_reference(&buffer->parent_ref, NULL); +} + +static struct wl_buffer_listener ss_buffer_listener = { + ss_buffer_release +}; + +static void +ss_frame_callback(void *data, struct wl_callback *callback, uint32_t time) +{ + struct nested_surface *surface = data; + struct nested_ss_surface *ss_surface = surface->renderer_data; + + flush_surface_frame_callback_list(surface, time); + + if (callback) + wl_callback_destroy(callback); + + ss_surface->frame_callback = NULL; +} + +static const struct wl_callback_listener ss_frame_listener = { + ss_frame_callback +}; + +static void +ss_surface_attach(struct nested_surface *surface, + struct nested_buffer *buffer) +{ + struct nested *nested = surface->nested; + struct nested_ss_surface *ss_surface = surface->renderer_data; + struct wl_buffer *parent_buffer; + const pixman_box32_t *rects; + int n_rects, i; + + if (buffer) { + /* Create a representation of the buffer in the parent + * compositor if we haven't already */ + if (buffer->parent_buffer == NULL) { + EGLDisplay *edpy = nested->egl_display; + EGLImageKHR image = surface->image; + + buffer->parent_buffer = + create_wayland_buffer_from_image(edpy, image); + + wl_buffer_add_listener(buffer->parent_buffer, + &ss_buffer_listener, + buffer); + } + + parent_buffer = buffer->parent_buffer; + + /* We'll take a reference to the buffer while the parent + * compositor is using it so that we won't report the release + * event until the parent has also finished with it */ + nested_buffer_reference(&buffer->parent_ref, buffer); + } else { + parent_buffer = NULL; + } + + wl_surface_attach(ss_surface->surface, parent_buffer, 0, 0); + + rects = pixman_region32_rectangles(&surface->pending.damage, &n_rects); + + for (i = 0; i < n_rects; i++) { + const pixman_box32_t *rect = rects + i; + wl_surface_damage(ss_surface->surface, + rect->x1, + rect->y1, + rect->x2 - rect->x1, + rect->y2 - rect->y1); + } + + if (ss_surface->frame_callback) + wl_callback_destroy(ss_surface->frame_callback); + + ss_surface->frame_callback = wl_surface_frame(ss_surface->surface); + wl_callback_add_listener(ss_surface->frame_callback, + &ss_frame_listener, + surface); + + wl_surface_commit(ss_surface->surface); +} + +static const struct nested_renderer +nested_ss_renderer = { + .surface_init = ss_surface_init, + .surface_fini = ss_surface_fini, + .render_clients = ss_render_clients, + .surface_attach = ss_surface_attach +}; + int main(int argc, char *argv[]) { struct display *display; struct nested *nested; + parse_options(nested_options, + ARRAY_LENGTH(nested_options), &argc, argv); + display = display_create(&argc, argv); if (display == NULL) { fprintf(stderr, "failed to create display: %m\n"); diff --git a/clients/resizor.c b/clients/resizor.c index 68e4bf9a..9cf1a3c6 100644 --- a/clients/resizor.c +++ b/clients/resizor.c @@ -195,7 +195,8 @@ key_handler(struct window *window, struct input *input, uint32_t time, } static void -menu_func(struct window *window, int index, void *user_data) +menu_func(struct window *window, + struct input *input, int index, void *user_data) { fprintf(stderr, "picked entry %d\n", index); } @@ -235,7 +236,7 @@ resizor_create(struct display *display) resizor = xzalloc(sizeof *resizor); resizor->window = window_create(display); - resizor->widget = frame_create(resizor->window, resizor); + resizor->widget = window_frame_create(resizor->window, resizor); window_set_title(resizor->window, "Wayland Resizor"); resizor->display = display; diff --git a/clients/scaler.c b/clients/scaler.c new file mode 100644 index 00000000..97530961 --- /dev/null +++ b/clients/scaler.c @@ -0,0 +1,215 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * Copyright © 2013 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include + +#include "window.h" +#include "scaler-client-protocol.h" + +#define BUFFER_SCALE 2 +static const int BUFFER_WIDTH = 421 * BUFFER_SCALE; +static const int BUFFER_HEIGHT = 337 * BUFFER_SCALE; +static const int SURFACE_WIDTH = 55 * 4; +static const int SURFACE_HEIGHT = 77 * 4; +static const double RECT_X = 21 * BUFFER_SCALE; /* buffer coords */ +static const double RECT_Y = 25 * BUFFER_SCALE; +static const double RECT_W = 55 * BUFFER_SCALE; +static const double RECT_H = 77 * BUFFER_SCALE; + +struct box { + struct display *display; + struct window *window; + struct widget *widget; + int width, height; + + struct wl_scaler *scaler; + struct wl_viewport *viewport; +}; + +static void +resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct box *box = data; + + /* Dont resize me */ + widget_set_size(box->widget, box->width, box->height); +} + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct box *box = data; + cairo_surface_t *surface; + cairo_t *cr; + + surface = window_get_surface(box->window); + if (surface == NULL || + cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { + fprintf(stderr, "failed to create cairo egl surface\n"); + return; + } + + cr = cairo_create(surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_line_width(cr, 1.0); + cairo_translate(cr, RECT_X, RECT_Y); + + /* red background */ + cairo_set_source_rgba(cr, 255, 0, 0, 255); + cairo_paint(cr); + + /* blue box */ + cairo_set_source_rgba(cr, 0, 0, 255, 255); + cairo_rectangle(cr, 0, 0, RECT_W, RECT_H); + cairo_fill(cr); + + /* black border outside the box */ + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_move_to(cr, 0, RECT_H + 0.5); + cairo_line_to(cr, RECT_W, RECT_H + 0.5); + cairo_stroke(cr); + + /* white border inside the box */ + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_move_to(cr, RECT_W - 0.5, 0); + cairo_line_to(cr, RECT_W - 0.5, RECT_H); + cairo_stroke(cr); + + /* the green border on inside the box, to be split half by crop */ + cairo_set_source_rgb(cr, 0, 1, 0); + cairo_move_to(cr, 0.5, RECT_H); + cairo_line_to(cr, 0.5, 0); + cairo_move_to(cr, 0, 0.5); + cairo_line_to(cr, RECT_W, 0.5); + cairo_stroke(cr); + + cairo_destroy(cr); + + /* TODO: buffer_transform */ + + cairo_surface_destroy(surface); +} + +static void +global_handler(struct display *display, uint32_t name, + const char *interface, uint32_t version, void *data) +{ + struct box *box = data; + wl_fixed_t src_x, src_y, src_width, src_height; + + /* Cut the green border in half, take white border fully in, + * and black border fully out. The borders are 1px wide in buffer. + * + * The gl-renderer uses linear texture sampling, this means the + * top and left edges go to 100% green, bottom goes to 50% blue/black, + * right edge has thick white sliding to 50% red. + */ + src_x = wl_fixed_from_double((RECT_X + 0.5) / BUFFER_SCALE); + src_y = wl_fixed_from_double((RECT_Y + 0.5) / BUFFER_SCALE); + src_width = wl_fixed_from_double((RECT_W - 0.5) / BUFFER_SCALE); + src_height = wl_fixed_from_double((RECT_H - 0.5) / BUFFER_SCALE); + + if (strcmp(interface, "wl_scaler") == 0) { + box->scaler = display_bind(display, name, + &wl_scaler_interface, 1); + + box->viewport = wl_scaler_get_viewport(box->scaler, + widget_get_wl_surface(box->widget)); + + wl_viewport_set(box->viewport, + src_x, src_y, src_width, src_height, + SURFACE_WIDTH, SURFACE_HEIGHT); /* dst */ + } +} + +static void +button_handler(struct widget *widget, + struct input *input, uint32_t time, + uint32_t button, enum wl_pointer_button_state state, void *data) +{ + struct box *box = data; + + if (button != BTN_LEFT) + return; + + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + window_move(box->window, input, + display_get_serial(box->display)); + } +} + +static void +touch_down_handler(struct widget *widget, struct input *input, + uint32_t serial, uint32_t time, int32_t id, + float x, float y, void *data) +{ + struct box *box = data; + window_move(box->window, input, + display_get_serial(box->display)); +} + +int +main(int argc, char *argv[]) +{ + struct box box; + struct display *d; + struct timeval tv; + + d = display_create(&argc, argv); + if (d == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + + gettimeofday(&tv, NULL); + srandom(tv.tv_usec); + + box.width = BUFFER_WIDTH / BUFFER_SCALE; + box.height = BUFFER_HEIGHT / BUFFER_SCALE; + box.display = d; + box.window = window_create(d); + box.widget = window_add_widget(box.window, &box); + window_set_title(box.window, "Scaler Test Box"); + window_set_buffer_scale(box.window, BUFFER_SCALE); + + widget_set_resize_handler(box.widget, resize_handler); + widget_set_redraw_handler(box.widget, redraw_handler); + widget_set_button_handler(box.widget, button_handler); + widget_set_default_cursor(box.widget, CURSOR_HAND1); + widget_set_touch_down_handler(box.widget, touch_down_handler); + + window_schedule_resize(box.window, box.width, box.height); + + display_set_user_data(box.display, &box); + display_set_global_handler(box.display, global_handler); + + display_run(d); + + window_destroy(box.window); + return 0; +} diff --git a/clients/simple-egl.c b/clients/simple-egl.c index 1fd41379..2c009ee2 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -87,12 +87,13 @@ struct window { GLuint col; } gl; + uint32_t benchmark_time, frames; struct wl_egl_window *native; struct wl_surface *surface; struct wl_shell_surface *shell_surface; EGLSurface egl_surface; struct wl_callback *callback; - int fullscreen, configured, opaque; + int fullscreen, configured, opaque, buffer_size, frame_sync; }; static const char *vert_shader_text = @@ -115,7 +116,7 @@ static const char *frag_shader_text = static int running = 1; static void -init_egl(struct display *display, int opaque) +init_egl(struct display *display, struct window *window) { static const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, @@ -128,15 +129,15 @@ init_egl(struct display *display, int opaque) EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, - EGL_ALPHA_SIZE, 1, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; - EGLint major, minor, n; + EGLint major, minor, n, count, i, size; + EGLConfig *configs; EGLBoolean ret; - if (opaque) + if (window->opaque || window->buffer_size == 16) config_attribs[9] = 0; display->egl.dpy = eglGetDisplay(display->display); @@ -147,9 +148,30 @@ init_egl(struct display *display, int opaque) ret = eglBindAPI(EGL_OPENGL_ES_API); assert(ret == EGL_TRUE); + if (!eglGetConfigs(display->egl.dpy, NULL, 0, &count) || count < 1) + assert(0); + + configs = calloc(count, sizeof *configs); + assert(configs); + ret = eglChooseConfig(display->egl.dpy, config_attribs, - &display->egl.conf, 1, &n); - assert(ret && n == 1); + configs, count, &n); + assert(ret && n >= 1); + + for (i = 0; i < n; i++) { + eglGetConfigAttrib(display->egl.dpy, + configs[i], EGL_BUFFER_SIZE, &size); + if (window->buffer_size == size) { + display->egl.conf = configs[i]; + break; + } + } + free(configs); + if (display->egl.conf == NULL) { + fprintf(stderr, "did not find config with buffer size %d\n", + window->buffer_size); + exit(EXIT_FAILURE); + } display->egl.ctx = eglCreateContext(display->egl.dpy, display->egl.conf, @@ -274,9 +296,6 @@ static const struct wl_shell_surface_listener shell_surface_listener = { handle_popup_done }; -static void -redraw(void *data, struct wl_callback *callback, uint32_t time); - static void configure_callback(void *data, struct wl_callback *callback, uint32_t time) { @@ -285,9 +304,6 @@ configure_callback(void *data, struct wl_callback *callback, uint32_t time) wl_callback_destroy(callback); window->configured = 1; - - if (window->callback == NULL) - redraw(data, NULL, time); } static struct wl_callback_listener configure_callback_listener = { @@ -295,7 +311,7 @@ static struct wl_callback_listener configure_callback_listener = { }; static void -toggle_fullscreen(struct window *window, int fullscreen) +set_fullscreen(struct window *window, int fullscreen) { struct wl_callback *callback; @@ -306,16 +322,18 @@ toggle_fullscreen(struct window *window, int fullscreen) wl_shell_surface_set_fullscreen(window->shell_surface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL); + callback = wl_display_sync(window->display->display); + wl_callback_add_listener(callback, + &configure_callback_listener, + window); + } else { wl_shell_surface_set_toplevel(window->shell_surface); handle_configure(window, window->shell_surface, 0, window->window_size.width, window->window_size.height); + window->configured = 1; } - - callback = wl_display_sync(window->display->display); - wl_callback_add_listener(callback, &configure_callback_listener, - window); } static void @@ -346,7 +364,10 @@ create_surface(struct window *window) window->egl_surface, window->display->egl.ctx); assert(ret == EGL_TRUE); - toggle_fullscreen(window, window->fullscreen); + if (!window->frame_sync) + eglSwapInterval(display->egl.dpy, 0); + + set_fullscreen(window, window->fullscreen); } static void @@ -391,11 +412,11 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; - static const int32_t speed_div = 5; - static uint32_t start_time = 0; + static const int32_t speed_div = 5, benchmark_interval = 5; struct wl_region *region; EGLint rect[4]; EGLint buffer_age = 0; + struct timeval tv; assert(window->callback == callback); window->callback = NULL; @@ -406,10 +427,20 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) if (!window->configured) return; - if (start_time == 0) - start_time = time; + gettimeofday(&tv, NULL); + time = tv.tv_sec * 1000 + tv.tv_usec / 1000; + if (window->frames == 0) + window->benchmark_time = time; + if (time - window->benchmark_time > (benchmark_interval * 1000)) { + printf("%d frames in %d seconds: %f fps\n", + window->frames, + benchmark_interval, + (float) window->frames / benchmark_interval); + window->benchmark_time = time; + window->frames = 0; + } - angle = ((time-start_time) / speed_div) % 360 * M_PI / 180.0; + angle = (time / speed_div) % 360 * M_PI / 180.0; rotation[0][0] = cos(angle); rotation[0][2] = sin(angle); rotation[2][0] = -sin(angle); @@ -448,9 +479,6 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) wl_surface_set_opaque_region(window->surface, NULL); } - window->callback = wl_surface_frame(window->surface); - wl_callback_add_listener(window->callback, &frame_listener, window); - if (display->swap_buffers_with_damage && buffer_age > 0) { rect[0] = window->geometry.width / 4 - 1; rect[1] = window->geometry.height / 4 - 1; @@ -462,6 +490,7 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) } else { eglSwapBuffers(display->egl.dpy, window->egl_surface); } + window->frames++; } static const struct wl_callback_listener frame_listener = { @@ -599,7 +628,7 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, struct display *d = data; if (key == KEY_F11 && state) - toggle_fullscreen(d->window, d->window->fullscreen ^ 1); + set_fullscreen(d->window, d->window->fullscreen ^ 1); else if (key == KEY_ESC && state) running = 0; } @@ -705,6 +734,8 @@ usage(int error_code) fprintf(stderr, "Usage: simple-egl [OPTIONS]\n\n" " -f\tRun in fullscreen mode\n" " -o\tCreate an opaque surface\n" + " -s\tUse a 16 bpp EGL config\n" + " -b\tDon't sync to compositor redraw (eglSwapInterval 0)\n" " -h\tThis help text\n\n"); exit(error_code); @@ -722,12 +753,18 @@ main(int argc, char **argv) display.window = &window; window.window_size.width = 250; window.window_size.height = 250; + window.buffer_size = 32; + window.frame_sync = 1; for (i = 1; i < argc; i++) { if (strcmp("-f", argv[i]) == 0) window.fullscreen = 1; else if (strcmp("-o", argv[i]) == 0) window.opaque = 1; + else if (strcmp("-s", argv[i]) == 0) + window.buffer_size = 16; + else if (strcmp("-b", argv[i]) == 0) + window.frame_sync = 0; else if (strcmp("-h", argv[i]) == 0) usage(EXIT_SUCCESS); else @@ -743,7 +780,7 @@ main(int argc, char **argv) wl_display_dispatch(display.display); - init_egl(&display, window.opaque); + init_egl(&display, &window); create_surface(&window); init_gl(&window); @@ -755,8 +792,16 @@ main(int argc, char **argv) sigint.sa_flags = SA_RESETHAND; sigaction(SIGINT, &sigint, NULL); - while (running && ret != -1) - ret = wl_display_dispatch(display.display); + /* The mainloop here is a little subtle. Redrawing will cause + * EGL to read events so we can just call + * wl_display_dispatch_pending() to handle any events that got + * queued up as a side effect. */ + while (running && ret != -1) { + wl_display_dispatch_pending(display.display); + while (!window.configured) + wl_display_dispatch(display.display); + redraw(&window, NULL, 0); + } fprintf(stderr, "simple-egl exiting\n"); diff --git a/clients/stacking.c b/clients/stacking.c new file mode 100644 index 00000000..544094c6 --- /dev/null +++ b/clients/stacking.c @@ -0,0 +1,312 @@ +/* + * Copyright © 2013 Collabora Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include "window.h" + +struct stacking { + struct display *display; + struct window *root_window; +}; + +static void +button_handler(struct widget *widget, + struct input *input, uint32_t time, + uint32_t button, + enum wl_pointer_button_state state, void *data); +static void +key_handler(struct window *window, + struct input *input, uint32_t time, + uint32_t key, uint32_t sym, enum wl_keyboard_key_state state, + void *data); +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data); +static void +fullscreen_handler(struct window *window, void *data); +static void +redraw_handler(struct widget *widget, void *data); + +/* Iff parent_window is set, the new window will be transient. */ +static struct window * +new_window(struct stacking *stacking, struct window *parent_window) +{ + struct window *new_window; + struct widget *new_widget; + + if (parent_window == NULL) { + new_window = window_create(stacking->display); + } else { + new_window = window_create_transient(stacking->display, + parent_window, 50, 50, 0); + } + + new_widget = window_frame_create(new_window, new_window); + + window_set_title(new_window, "Stacking Test"); + window_set_key_handler(new_window, key_handler); + window_set_keyboard_focus_handler(new_window, keyboard_focus_handler); + window_set_fullscreen_handler(new_window, fullscreen_handler); + widget_set_button_handler(new_widget, button_handler); + widget_set_redraw_handler(new_widget, redraw_handler); + window_set_user_data(new_window, stacking); + + window_schedule_resize(new_window, 300, 300); + + return new_window; +} + +static void +show_popup_cb(struct window *window, struct input *input, int index, void *data) +{ + /* Ignore the selected menu item. */ +} + +static void +show_popup(struct stacking *stacking, struct input *input, uint32_t time, + struct window *window) +{ + int32_t x, y; + static const char *entries[] = { + "Test Entry", + "Another Test Entry", + }; + + input_get_position(input, &x, &y); + window_show_menu(stacking->display, input, time, window, x, y, + show_popup_cb, entries, ARRAY_LENGTH(entries)); +} + +static void +button_handler(struct widget *widget, + struct input *input, uint32_t time, + uint32_t button, + enum wl_pointer_button_state state, void *data) +{ + struct stacking *stacking = data; + + switch (button) { + case BTN_RIGHT: + if (state == WL_POINTER_BUTTON_STATE_PRESSED) + show_popup(stacking, input, time, + widget_get_user_data(widget)); + break; + + case BTN_LEFT: + default: + break; + } +} + +static void +key_handler(struct window *window, + struct input *input, uint32_t time, + uint32_t key, uint32_t sym, enum wl_keyboard_key_state state, + void *data) +{ + struct stacking *stacking = data; + + if (state != WL_KEYBOARD_KEY_STATE_PRESSED) + return; + + switch (sym) { + case XKB_KEY_f: + fullscreen_handler(window, data); + break; + + case XKB_KEY_m: + window_set_maximized(window, !window_is_maximized(window)); + break; + + case XKB_KEY_n: + /* New top-level window. */ + new_window(stacking, NULL); + break; + + case XKB_KEY_p: + show_popup(stacking, input, time, window); + break; + + case XKB_KEY_q: + exit (0); + break; + + case XKB_KEY_t: + /* New transient window. */ + new_window(stacking, window); + break; + + default: + break; + } +} + +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data) +{ + window_schedule_redraw(window); +} + +static void +fullscreen_handler(struct window *window, void *data) +{ + window_set_fullscreen(window, !window_is_fullscreen(window)); +} + +static void +draw_string(cairo_t *cr, + const char *fmt, ...) __attribute__((format (gnu_printf, 2, 3))); + +static void +draw_string(cairo_t *cr, + const char *fmt, ...) +{ + char buffer[4096]; + char *p, *end; + va_list argp; + cairo_text_extents_t text_extents; + cairo_font_extents_t font_extents; + + cairo_save(cr); + + cairo_select_font_face(cr, "sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 14); + + cairo_font_extents(cr, &font_extents); + + va_start(argp, fmt); + + vsnprintf(buffer, sizeof(buffer), fmt, argp); + + p = buffer; + while (*p) { + end = strchr(p, '\n'); + if (end) + *end = 0; + + cairo_show_text(cr, p); + cairo_text_extents(cr, p, &text_extents); + cairo_rel_move_to(cr, -text_extents.x_advance, font_extents.height); + + if (end) + p = end + 1; + else + break; + } + + va_end(argp); + + cairo_restore(cr); +} + +static void +set_window_background_colour(cairo_t *cr, struct window *window) +{ + if (window_is_transient(window)) + cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.4); + else if (window_is_maximized(window)) + cairo_set_source_rgba(cr, 1.0, 1.0, 0.0, 0.6); + else if (window_is_fullscreen(window)) + cairo_set_source_rgba(cr, 0.0, 1.0, 1.0, 0.6); + else + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); +} + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct window *window; + struct rectangle allocation; + cairo_t *cr; + + widget_get_allocation(widget, &allocation); + window = widget_get_user_data(widget); + + cr = widget_cairo_create(widget); + cairo_translate(cr, allocation.x, allocation.y); + + /* Draw background. */ + cairo_push_group(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + set_window_background_colour(cr, window); + cairo_rectangle(cr, 0, 0, allocation.width, allocation.height); + cairo_fill(cr); + + cairo_pop_group_to_source(cr); + cairo_paint(cr); + + /* Print the instructions. */ + cairo_move_to(cr, 5, 15); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + + draw_string(cr, + "Window: %p\n" + "Fullscreen? %u\n" + "Maximized? %u\n" + "Transient? %u\n" + "Keys: (f)ullscreen, (m)aximize,\n" + " (n)ew window, (p)opup,\n" + " (q)uit, (t)ransient window\n", + window, window_is_fullscreen(window), + window_is_maximized(window), window_is_transient(window)); + + cairo_destroy(cr); +} + +int +main(int argc, char *argv[]) +{ + struct stacking stacking; + + memset(&stacking, 0, sizeof stacking); + +#ifdef HAVE_PANGO + g_type_init(); +#endif + + stacking.display = display_create(&argc, argv); + if (stacking.display == NULL) { + fprintf(stderr, "Failed to create display: %m\n"); + return -1; + } + + display_set_user_data(stacking.display, &stacking); + + stacking.root_window = new_window(&stacking, NULL); + + display_run(stacking.display); + + return 0; +} diff --git a/clients/subsurfaces.c b/clients/subsurfaces.c index 101ff17e..45cc44b4 100644 --- a/clients/subsurfaces.c +++ b/clients/subsurfaces.c @@ -498,6 +498,7 @@ triangle_create(struct window *window, struct egl_state *egl) tri->egl = egl; tri->widget = window_add_subsurface(window, tri, int_to_mode(option_triangle_mode)); + widget_set_use_cairo(tri->widget, 0); widget_set_resize_handler(tri->widget, triangle_resize_handler); widget_set_redraw_handler(tri->widget, triangle_redraw_handler); @@ -722,7 +723,7 @@ demoapp_create(struct display *display) display_set_user_data(app->display, app); app->window = window_create(app->display); - app->widget = frame_create(app->window, app); + app->widget = window_frame_create(app->window, app); window_set_title(app->window, "Wayland Sub-surface Demo"); window_set_key_handler(app->window, key_handler); diff --git a/clients/tablet-shell.c b/clients/tablet-shell.c deleted file mode 100644 index 45733b11..00000000 --- a/clients/tablet-shell.c +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright © 2011, 2012 Intel Corporation - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that copyright - * notice and this permission notice appear in supporting documentation, and - * that the name of the copyright holders not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission. The copyright holders make no representations - * about the suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -#include "window.h" -#include "../shared/cairo-util.h" -#include "../shared/config-parser.h" - -#include "tablet-shell-client-protocol.h" - -struct tablet { - struct display *display; - struct tablet_shell *tablet_shell; - struct rectangle allocation; - struct window *switcher; - - struct homescreen *homescreen; - struct lockscreen *lockscreen; -}; - -struct homescreen { - struct window *window; - struct widget *widget; - struct wl_list launcher_list; -}; - -struct lockscreen { - struct window *window; - struct widget *widget; -}; - -struct launcher { - struct widget *widget; - struct homescreen *homescreen; - cairo_surface_t *icon; - int focused, pressed; - char *path; - struct wl_list link; -}; - -static char *key_lockscreen_icon; -static char *key_lockscreen_background; -static char *key_homescreen_background; - -static void -sigchild_handler(int s) -{ - int status; - pid_t pid; - - while (pid = waitpid(-1, &status, WNOHANG), pid > 0) - fprintf(stderr, "child %d exited\n", pid); -} - -static void -paint_background(cairo_t *cr, const char *path, struct rectangle *allocation) -{ - cairo_surface_t *image = NULL; - cairo_pattern_t *pattern; - cairo_matrix_t matrix; - double sx, sy; - - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - if (path) - image = load_cairo_surface(path); - if (image) { - pattern = cairo_pattern_create_for_surface(image); - sx = (double) cairo_image_surface_get_width(image) / - allocation->width; - sy = (double) cairo_image_surface_get_height(image) / - allocation->height; - cairo_matrix_init_scale(&matrix, sx, sy); - cairo_pattern_set_matrix(pattern, &matrix); - cairo_set_source(cr, pattern); - cairo_pattern_destroy (pattern); - cairo_surface_destroy(image); - cairo_paint(cr); - } else { - fprintf(stderr, "couldn't load background image: %s\n", path); - cairo_set_source_rgb(cr, 0.2, 0, 0); - cairo_paint(cr); - } -} - -static void -homescreen_draw(struct widget *widget, void *data) -{ - struct homescreen *homescreen = data; - cairo_surface_t *surface; - struct rectangle allocation; - cairo_t *cr; - struct launcher *launcher; - const int rows = 4, columns = 5, icon_width = 128, icon_height = 128; - int x, y, i, width, height, vmargin, hmargin, vpadding, hpadding; - - surface = window_get_surface(homescreen->window); - cr = cairo_create(surface); - - widget_get_allocation(widget, &allocation); - paint_background(cr, key_homescreen_background, &allocation); - - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - - width = allocation.width - columns * icon_width; - hpadding = width / (columns + 1); - hmargin = (width - hpadding * (columns - 1)) / 2; - - height = allocation.height - rows * icon_height; - vpadding = height / (rows + 1); - vmargin = (height - vpadding * (rows - 1)) / 2; - - x = hmargin; - y = vmargin; - i = 0; - - wl_list_for_each(launcher, &homescreen->launcher_list, link) { - widget_set_allocation(launcher->widget, - x, y, icon_width, icon_height); - x += icon_width + hpadding; - i++; - if (i == columns) { - x = hmargin; - y += icon_height + vpadding; - i = 0; - } - } - - cairo_destroy(cr); - cairo_surface_destroy(surface); -} - -static void -lockscreen_draw(struct widget *widget, void *data) -{ - struct lockscreen *lockscreen = data; - cairo_surface_t *surface; - cairo_surface_t *icon; - struct rectangle allocation; - cairo_t *cr; - int width, height; - - surface = window_get_surface(lockscreen->window); - cr = cairo_create(surface); - - widget_get_allocation(widget, &allocation); - paint_background(cr, key_lockscreen_background, &allocation); - - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - icon = load_cairo_surface(key_lockscreen_icon); - if (icon) { - width = cairo_image_surface_get_width(icon); - height = cairo_image_surface_get_height(icon); - cairo_set_source_surface(cr, icon, - allocation.x + (allocation.width - width) / 2, - allocation.y + (allocation.height - height) / 2); - } else { - fprintf(stderr, "couldn't load lockscreen icon: %s\n", - key_lockscreen_icon); - cairo_set_source_rgb(cr, 0.2, 0, 0); - } - cairo_paint(cr); - cairo_destroy(cr); - cairo_surface_destroy(icon); - cairo_surface_destroy(surface); -} - -static void -lockscreen_button_handler(struct widget *widget, - struct input *input, uint32_t time, - uint32_t button, - enum wl_pointer_button_state state, void *data) -{ - struct lockscreen *lockscreen = data; - - if (state == WL_POINTER_BUTTON_STATE_PRESSED && lockscreen->window) { - window_destroy(lockscreen->window); - lockscreen->window = NULL; - } -} - -static struct homescreen * -homescreen_create(struct tablet *tablet) -{ - struct homescreen *homescreen; - - homescreen = zalloc (sizeof *homescreen); - homescreen->window = window_create_custom(tablet->display); - homescreen->widget = - window_add_widget(homescreen->window, homescreen); - window_set_user_data(homescreen->window, homescreen); - window_set_title(homescreen->window, "homescreen"); - widget_set_redraw_handler(homescreen->widget, homescreen_draw); - - return homescreen; -} - -static struct lockscreen * -lockscreen_create(struct tablet *tablet) -{ - struct lockscreen *lockscreen; - - lockscreen = zalloc (sizeof *lockscreen); - lockscreen->window = window_create_custom(tablet->display); - lockscreen->widget = - window_add_widget(lockscreen->window, lockscreen); - window_set_user_data(lockscreen->window, lockscreen); - window_set_title(lockscreen->window, "lockscreen"); - widget_set_redraw_handler(lockscreen->widget, lockscreen_draw); - widget_set_button_handler(lockscreen->widget, - lockscreen_button_handler); - - return lockscreen; -} - -static void -show_lockscreen(void *data, struct tablet_shell *tablet_shell) -{ - struct tablet *tablet = data; - - tablet->lockscreen = lockscreen_create(tablet); - tablet_shell_set_lockscreen(tablet->tablet_shell, - window_get_wl_surface(tablet->lockscreen->window)); - - widget_schedule_resize(tablet->lockscreen->widget, - tablet->allocation.width, - tablet->allocation.height); -} - -static void -show_switcher(void *data, struct tablet_shell *tablet_shell) -{ - struct tablet *tablet = data; - - tablet->switcher = window_create_custom(tablet->display); - window_set_user_data(tablet->switcher, tablet); - tablet_shell_set_switcher(tablet->tablet_shell, - window_get_wl_surface(tablet->switcher)); -} - -static void -hide_switcher(void *data, struct tablet_shell *tablet_shell) -{ -} - -static const struct tablet_shell_listener tablet_shell_listener = { - show_lockscreen, - show_switcher, - hide_switcher -}; - -static int -launcher_enter_handler(struct widget *widget, struct input *input, - float x, float y, void *data) -{ - struct launcher *launcher = data; - - launcher->focused = 1; - widget_schedule_redraw(widget); - - return CURSOR_LEFT_PTR; -} - -static void -launcher_leave_handler(struct widget *widget, - struct input *input, void *data) -{ - struct launcher *launcher = data; - - launcher->focused = 0; - widget_schedule_redraw(widget); -} - -static void -launcher_activate(struct launcher *widget) -{ - pid_t pid; - - pid = fork(); - if (pid < 0) { - fprintf(stderr, "fork failed: %m\n"); - return; - } - - if (pid) - return; - - if (execl(widget->path, widget->path, NULL) < 0) { - fprintf(stderr, "execl '%s' failed: %m\n", widget->path); - exit(1); - } -} - -static void -launcher_button_handler(struct widget *widget, - struct input *input, uint32_t time, - uint32_t button, - enum wl_pointer_button_state state, void *data) -{ - struct launcher *launcher; - - launcher = widget_get_user_data(widget); - widget_schedule_redraw(widget); - if (state == WL_POINTER_BUTTON_STATE_RELEASED) { - launcher_activate(launcher); - launcher->pressed = 0; - } else if (state == WL_POINTER_BUTTON_STATE_PRESSED) - launcher->pressed = 1; -} - -static void -launcher_redraw_handler(struct widget *widget, void *data) -{ - struct launcher *launcher = data; - cairo_surface_t *surface; - struct rectangle allocation; - cairo_t *cr; - - surface = window_get_surface(launcher->homescreen->window); - cr = cairo_create(surface); - - widget_get_allocation(widget, &allocation); - if (launcher->pressed) { - allocation.x++; - allocation.y++; - } - - cairo_set_source_surface(cr, launcher->icon, - allocation.x, allocation.y); - cairo_paint(cr); - - if (launcher->focused) { - cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.4); - cairo_mask_surface(cr, launcher->icon, - allocation.x, allocation.y); - } - - cairo_destroy(cr); -} - -static void -tablet_shell_add_launcher(struct tablet *tablet, - const char *icon, const char *path) -{ - struct launcher *launcher; - struct homescreen *homescreen = tablet->homescreen; - - launcher = xmalloc(sizeof *launcher); - launcher->icon = load_cairo_surface(icon); - if ( !launcher->icon || - cairo_surface_status (launcher->icon) != CAIRO_STATUS_SUCCESS) { - fprintf(stderr, "couldn't load %s\n", icon); - free(launcher); - return; - } - launcher->path = strdup(path); - - launcher->homescreen = homescreen; - launcher->widget = widget_add_widget(homescreen->widget, launcher); - widget_set_enter_handler(launcher->widget, - launcher_enter_handler); - widget_set_leave_handler(launcher->widget, - launcher_leave_handler); - widget_set_button_handler(launcher->widget, - launcher_button_handler); - widget_set_redraw_handler(launcher->widget, - launcher_redraw_handler); - - wl_list_insert(&homescreen->launcher_list, &launcher->link); -} - -static void -global_handler(struct display *display, uint32_t name, - const char *interface, uint32_t version, void *data) -{ - struct tablet *tablet = data; - - if (!strcmp(interface, "tablet_shell")) { - tablet->tablet_shell = - display_bind(display, name, - &tablet_shell_interface, 1); - tablet_shell_add_listener(tablet->tablet_shell, - &tablet_shell_listener, tablet); - } -} - -int main(int argc, char *argv[]) -{ - struct tablet tablet = { 0 }; - struct display *display; - struct output *output; - struct weston_config *config; - struct weston_config_section *s; - char *icon, *path; - const char *name; - - display = display_create(&argc, argv); - if (display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); - return -1; - } - - tablet.display = display; - - display_set_user_data(tablet.display, &tablet); - display_set_global_handler(tablet.display, global_handler); - - tablet.homescreen = homescreen_create(&tablet); - tablet_shell_set_homescreen(tablet.tablet_shell, - window_get_wl_surface(tablet.homescreen->window)); - - wl_display_roundtrip (display_get_display(tablet.display)); - - wl_list_init(&tablet.homescreen->launcher_list); - - config = weston_config_parse("weston.ini"); - s = weston_config_get_section(config, "shell", NULL, NULL); - weston_config_section_get_string(s, "lockscreen-icon", - &key_lockscreen_icon, NULL); - weston_config_section_get_string(s, "lockscreen", - &key_lockscreen_background, NULL); - weston_config_section_get_string(s, "homescreen", - &key_homescreen_background, NULL); - - s = NULL; - while (weston_config_next_section(config, &s, &name)) { - if (strcmp(name, "launcher") != 0) - continue; - - weston_config_section_get_string(s, "icon", &icon, NULL); - weston_config_section_get_string(s, "path", &path, NULL); - - if (icon != NULL && path != NULL) - tablet_shell_add_launcher(&tablet, icon, path); - else - fprintf(stderr, "invalid launcher section\n"); - - free(icon); - free(path); - } - - weston_config_destroy(config); - - signal(SIGCHLD, sigchild_handler); - - output = display_get_output(tablet.display); - output_get_allocation(output, &tablet.allocation); - widget_schedule_resize(tablet.homescreen->widget, - tablet.allocation.width, - tablet.allocation.height); - display_run(display); - - return 0; -} diff --git a/clients/terminal.c b/clients/terminal.c index cec1d67e..e2a6236c 100644 --- a/clients/terminal.c +++ b/clients/terminal.c @@ -38,6 +38,8 @@ #include #include +#include + #include #include "../shared/config-parser.h" @@ -426,6 +428,7 @@ struct terminal { struct window *window; struct widget *widget; struct display *display; + char *title; union utf8_char *data; struct task io_task; char *tab_ruler; @@ -441,8 +444,11 @@ struct terminal { character_set saved_cs, saved_g0, saved_g1; keyboard_mode key_mode; int data_pitch, attr_pitch; /* The width in bytes of a line */ - int width, height, start, row, column; + int width, height, row, column, max_width; + uint32_t buffer_height; + uint32_t start, end, saved_start, log_size; int saved_row, saved_column; + int scrolling; int send_cursor_position; int fd, master; uint32_t modifiers; @@ -461,7 +467,7 @@ struct terminal { uint32_t hide_cursor_serial; struct wl_data_source *selection; - uint32_t button_time; + uint32_t click_time; int dragging, click_count; int selection_start_x, selection_start_y; int selection_end_x, selection_end_y; @@ -545,9 +551,9 @@ terminal_get_row(struct terminal *terminal, int row) { int index; - index = (row + terminal->start) % terminal->height; + index = (row + terminal->start) & (terminal->buffer_height - 1); - return &terminal->data[index * terminal->width]; + return (void *) terminal->data + index * terminal->data_pitch; } static struct attr* @@ -555,9 +561,9 @@ terminal_get_attr_row(struct terminal *terminal, int row) { int index; - index = (row + terminal->start) % terminal->height; + index = (row + terminal->start) & (terminal->buffer_height - 1); - return &terminal->data_attr[index * terminal->width]; + return (void *) terminal->data_attr + index * terminal->attr_pitch; } union decoded_attr { @@ -620,18 +626,16 @@ terminal_scroll_buffer(struct terminal *terminal, int d) { int i; - d = d % (terminal->height + 1); - terminal->start = (terminal->start + d) % terminal->height; - if (terminal->start < 0) terminal->start = terminal->height + terminal->start; - if(d < 0) { + terminal->start += d; + if (d < 0) { d = 0 - d; - for(i = 0; i < d; i++) { + for (i = 0; i < d; i++) { memset(terminal_get_row(terminal, i), 0, terminal->data_pitch); attr_init(terminal_get_attr_row(terminal, i), terminal->curr_attr, terminal->width); } } else { - for(i = terminal->height - d; i < terminal->height; i++) { + for (i = terminal->height - d; i < terminal->height; i++) { memset(terminal_get_row(terminal, i), 0, terminal->data_pitch); attr_init(terminal_get_attr_row(terminal, i), terminal->curr_attr, terminal->width); @@ -733,68 +737,90 @@ terminal_shift_line(struct terminal *terminal, int d) } static void -terminal_resize_cells(struct terminal *terminal, int width, int height) +terminal_resize_cells(struct terminal *terminal, + int width, int height) { - size_t size; union utf8_char *data; struct attr *data_attr; char *tab_ruler; int data_pitch, attr_pitch; int i, l, total_rows; + uint32_t d, uheight = height; struct rectangle allocation; struct winsize ws; + if (uheight > terminal->buffer_height) + height = terminal->buffer_height; + if (terminal->width == width && terminal->height == height) return; - data_pitch = width * sizeof(union utf8_char); - size = data_pitch * height; - data = zalloc(size); - attr_pitch = width * sizeof(struct attr); - data_attr = malloc(attr_pitch * height); - tab_ruler = zalloc(width); - attr_init(data_attr, terminal->curr_attr, width * height); - if (terminal->data && terminal->data_attr) { - if (width > terminal->width) - l = terminal->width; - else - l = width; - - if (terminal->height > height) { - total_rows = height; - i = 1 + terminal->row - height; - if (i > 0) { - terminal->start = (terminal->start + i) % terminal->height; - terminal->row = terminal->row - i; - } - } else { - total_rows = terminal->height; + if (terminal->data && width <= terminal->max_width) { + d = 0; + if (height < terminal->height && height <= terminal->row) + d = terminal->height - height; + else if (height > terminal->height && + terminal->height - 1 == terminal->row) { + d = terminal->height - height; + if (terminal->log_size < uheight) + d = -terminal->start; } - for (i = 0; i < total_rows; i++) { - memcpy(&data[width * i], - terminal_get_row(terminal, i), - l * sizeof(union utf8_char)); - memcpy(&data_attr[width * i], - terminal_get_attr_row(terminal, i), - l * sizeof(struct attr)); + terminal->start += d; + terminal->row -= d; + } else { + terminal->max_width = width; + data_pitch = width * sizeof(union utf8_char); + data = zalloc(data_pitch * terminal->buffer_height); + attr_pitch = width * sizeof(struct attr); + data_attr = malloc(attr_pitch * terminal->buffer_height); + tab_ruler = zalloc(width); + attr_init(data_attr, terminal->curr_attr, + width * terminal->buffer_height); + + if (terminal->data && terminal->data_attr) { + if (width > terminal->width) + l = terminal->width; + else + l = width; + + if (terminal->height > height) { + total_rows = height; + i = 1 + terminal->row - height; + if (i > 0) { + terminal->start += i; + terminal->row = terminal->row - i; + } + } else { + total_rows = terminal->height; + } + + for (i = 0; i < total_rows; i++) { + memcpy(&data[width * i], + terminal_get_row(terminal, i), + l * sizeof(union utf8_char)); + memcpy(&data_attr[width * i], + terminal_get_attr_row(terminal, i), + l * sizeof(struct attr)); + } + + free(terminal->data); + free(terminal->data_attr); + free(terminal->tab_ruler); } - free(terminal->data); - free(terminal->data_attr); - free(terminal->tab_ruler); + terminal->data_pitch = data_pitch; + terminal->attr_pitch = attr_pitch; + terminal->data = data; + terminal->data_attr = data_attr; + terminal->tab_ruler = tab_ruler; + terminal->start = 0; } - terminal->data_pitch = data_pitch; - terminal->attr_pitch = attr_pitch; terminal->margin_bottom = height - (terminal->height - terminal->margin_bottom); terminal->width = width; terminal->height = height; - terminal->data = data; - terminal->data_attr = data_attr; - terminal->tab_ruler = tab_ruler; - terminal->start = 0; terminal_init_tabs(terminal); /* Update the window size */ @@ -812,7 +838,7 @@ resize_handler(struct widget *widget, { struct terminal *terminal = data; int32_t columns, rows, m; - + char *p; m = 2 * terminal->margin; columns = (width - m) / (int32_t) terminal->average_width; rows = (height - m) / (int32_t) terminal->extents.height; @@ -822,6 +848,9 @@ resize_handler(struct widget *widget, width = columns * terminal->average_width + m; height = rows * terminal->extents.height + m; widget_set_size(terminal->widget, width, height); + asprintf(&p, "%s — [%dx%d]", terminal->title, columns, rows); + window_set_title(terminal->window, p); + free(p); } terminal_resize_cells(terminal, columns, rows); @@ -840,7 +869,7 @@ terminal_resize(struct terminal *terminal, int columns, int rows) width = columns * terminal->average_width + m; height = rows * terminal->extents.height + m; - frame_set_child_size(terminal->widget, width, height); + window_frame_set_child_size(terminal->widget, width, height); } struct color_scheme DEFAULT_COLORS = { @@ -1232,6 +1261,8 @@ handle_osc(struct terminal *terminal) case 0: /* Icon name and window title */ case 1: /* Icon label */ case 2: /* Window title*/ + free(terminal->title); + terminal->title = strdup(p); window_set_title(terminal->window, p); break; case 7: /* shell cwd as uri */ @@ -1383,12 +1414,9 @@ handle_escape(struct terminal *terminal) terminal->curr_attr, terminal->width); } } else if (args[0] == 2) { - for (i = 0; i < terminal->height; i++) { - memset(terminal_get_row(terminal, i), - 0, terminal->data_pitch); - attr_init(terminal_get_attr_row(terminal, i), - terminal->curr_attr, terminal->width); - } + /* Clear screen by scrolling contents out */ + terminal_scroll_buffer(terminal, + terminal->end - terminal->start); } break; case 'K': /* EL */ @@ -1827,7 +1855,7 @@ handle_special_char(struct terminal *terminal, char c) case '\v': case '\f': terminal->row++; - if(terminal->row > terminal->margin_bottom) { + if (terminal->row > terminal->margin_bottom) { terminal->row = terminal->margin_bottom; terminal_scroll(terminal, +1); } @@ -1930,6 +1958,13 @@ handle_char(struct terminal *terminal, union utf8_char utf8) row[terminal->column] = utf8; attr_row[terminal->column++] = terminal->curr_attr; + if (terminal->row + terminal->start + 1 > terminal->end) + terminal->end = terminal->row + terminal->start + 1; + if (terminal->end == terminal->buffer_height) + terminal->log_size = terminal->buffer_height; + else if (terminal->log_size < terminal->buffer_height) + terminal->log_size = terminal->end; + /* cursor jump for wide character. */ if (is_wide(utf8)) row[terminal->column++].ch = 0x200B; /* space glyph */ @@ -2177,45 +2212,90 @@ fullscreen_handler(struct window *window, void *data) } static void -close_handler(struct window *window, void *data) +close_handler(void *data) { struct terminal *terminal = data; terminal_destroy(terminal); } +static void +terminal_copy(struct terminal *terminal, struct input *input) +{ + terminal->selection = + display_create_data_source(terminal->display); + wl_data_source_offer(terminal->selection, + "text/plain;charset=utf-8"); + wl_data_source_add_listener(terminal->selection, + &data_source_listener, terminal); + input_set_selection(input, terminal->selection, + display_get_serial(terminal->display)); +} + +static void +terminal_paste(struct terminal *terminal, struct input *input) +{ + input_receive_selection_data_to_fd(input, + "text/plain;charset=utf-8", + terminal->master); + +} + +static void +terminal_new_instance(struct terminal *terminal) +{ + struct terminal *new_terminal; + + new_terminal = terminal_create(terminal->display); + if (terminal_run(new_terminal, option_shell)) + terminal_destroy(new_terminal); +} + static int handle_bound_key(struct terminal *terminal, struct input *input, uint32_t sym, uint32_t time) { - struct terminal *new_terminal; - switch (sym) { case XKB_KEY_X: /* Cut selection; terminal doesn't do cut, fall * through to copy. */ case XKB_KEY_C: - terminal->selection = - display_create_data_source(terminal->display); - wl_data_source_offer(terminal->selection, - "text/plain;charset=utf-8"); - wl_data_source_add_listener(terminal->selection, - &data_source_listener, terminal); - input_set_selection(input, terminal->selection, - display_get_serial(terminal->display)); + terminal_copy(terminal, input); return 1; case XKB_KEY_V: - input_receive_selection_data_to_fd(input, - "text/plain;charset=utf-8", - terminal->master); + terminal_paste(terminal, input); + return 1; + case XKB_KEY_N: + terminal_new_instance(terminal); + return 1; + + case XKB_KEY_Up: + if (!terminal->scrolling) + terminal->saved_start = terminal->start; + if (terminal->start == terminal->end - terminal->log_size) + return 1; + terminal->scrolling = 1; + terminal->start--; + terminal->row++; + terminal->selection_start_row++; + terminal->selection_end_row++; + widget_schedule_redraw(terminal->widget); return 1; - case XKB_KEY_N: - new_terminal = terminal_create(terminal->display); - if (terminal_run(new_terminal, option_shell)) - terminal_destroy(new_terminal); + case XKB_KEY_Down: + if (!terminal->scrolling) + terminal->saved_start = terminal->start; + + if (terminal->start == terminal->saved_start) + return 1; + terminal->scrolling = 1; + terminal->start++; + terminal->row--; + terminal->selection_start_row--; + terminal->selection_end_row--; + widget_schedule_redraw(terminal->widget); return 1; default: @@ -2231,7 +2311,7 @@ key_handler(struct window *window, struct input *input, uint32_t time, struct terminal *terminal = data; char ch[MAX_RESPONSE]; uint32_t modifiers, serial; - int ret, len = 0; + int ret, len = 0, d; bool convert_utf8 = true; modifiers = input_get_modifiers(input); @@ -2435,6 +2515,16 @@ key_handler(struct window *window, struct input *input, uint32_t time, } if (state == WL_KEYBOARD_KEY_STATE_PRESSED && len > 0) { + if (terminal->scrolling) { + d = terminal->saved_start - terminal->start; + terminal->row -= d; + terminal->selection_start_row -= d; + terminal->selection_end_row -= d; + terminal->start = terminal->saved_start; + terminal->scrolling = 0; + widget_schedule_redraw(terminal->widget); + } + terminal_write(terminal, ch, len); /* Hide cursor, except if this was coming from a @@ -2563,6 +2653,59 @@ recompute_selection(struct terminal *terminal) return 1; } +static void +menu_func(struct window *window, struct input *input, int index, void *data) +{ + struct terminal *terminal = data; + + fprintf(stderr, "picked entry %d\n", index); + + switch (index) { + case 0: + terminal_new_instance(terminal); + break; + case 1: + terminal_copy(terminal, input); + break; + case 2: + terminal_paste(terminal, input); + break; + } +} + +static void +show_menu(struct terminal *terminal, struct input *input, uint32_t time) +{ + int32_t x, y; + static const char *entries[] = { + "Open Terminal", "Copy", "Paste" + }; + + input_get_position(input, &x, &y); + window_show_menu(terminal->display, input, time, terminal->window, + x - 10, y - 10, menu_func, + entries, ARRAY_LENGTH(entries)); +} + +static void +click_handler(struct widget *widget, struct terminal *terminal, + struct input *input, int32_t x, int32_t y, + uint32_t time) +{ + if (time - terminal->click_time < 500) + terminal->click_count++; + else + terminal->click_count = 1; + + terminal->click_time = time; + terminal->dragging = (terminal->click_count - 1) % 3 + SELECT_CHAR; + + terminal->selection_end_x = terminal->selection_start_x = x; + terminal->selection_end_y = terminal->selection_start_y = y; + if (recompute_selection(terminal)) + widget_schedule_redraw(widget); +} + static void button_handler(struct widget *widget, struct input *input, uint32_t time, @@ -2570,31 +2713,22 @@ button_handler(struct widget *widget, enum wl_pointer_button_state state, void *data) { struct terminal *terminal = data; + int32_t x, y; switch (button) { - case 272: + case BTN_LEFT: if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - - if (time - terminal->button_time < 500) - terminal->click_count++; - else - terminal->click_count = 1; - - terminal->button_time = time; - terminal->dragging = - (terminal->click_count - 1) % 3 + SELECT_CHAR; - - input_get_position(input, - &terminal->selection_start_x, - &terminal->selection_start_y); - terminal->selection_end_x = terminal->selection_start_x; - terminal->selection_end_y = terminal->selection_start_y; - if (recompute_selection(terminal)) - widget_schedule_redraw(widget); + input_get_position(input, &x, &y); + click_handler(widget, terminal, input, x, y, time); } else { terminal->dragging = SELECT_NONE; } break; + + case BTN_RIGHT: + if (state == WL_POINTER_BUTTON_STATE_PRESSED) + show_menu(terminal, input, time); + break; } } @@ -2602,6 +2736,11 @@ static int enter_handler(struct widget *widget, struct input *input, float x, float y, void *data) { + struct terminal *terminal = data; + + /* Reset title to get rid of resizing '[WxH]' in titlebar */ + window_set_title(terminal->window, terminal->title); + return CURSOR_IBEAM; } @@ -2634,6 +2773,43 @@ output_handler(struct window *window, struct output *output, int enter, window_schedule_redraw(window); } +static void +touch_down_handler(struct widget *widget, struct input *input, + uint32_t serial, uint32_t time, int32_t id, + float x, float y, void *data) +{ + struct terminal *terminal = data; + + if (id == 0) + click_handler(widget, terminal, input, x, y, time); +} + +static void +touch_up_handler(struct widget *widget, struct input *input, + uint32_t serial, uint32_t time, int32_t id, void *data) +{ + struct terminal *terminal = data; + + if (id == 0) + terminal->dragging = SELECT_NONE; +} + +static void +touch_motion_handler(struct widget *widget, struct input *input, + uint32_t time, int32_t id, float x, float y, void *data) +{ + struct terminal *terminal = data; + + if (terminal->dragging && + id == 0) { + terminal->selection_end_x = (int)x; + terminal->selection_end_y = (int)y; + + if (recompute_selection(terminal)) + widget_schedule_redraw(widget); + } +} + #ifndef howmany #define howmany(x, y) (((x) + ((y) - 1)) / (y)) #endif @@ -2652,8 +2828,9 @@ terminal_create(struct display *display) terminal->margin_top = 0; terminal->margin_bottom = -1; terminal->window = window_create(display); - terminal->widget = frame_create(terminal->window, terminal); - window_set_title(terminal->window, "Wayland Terminal"); + terminal->widget = window_frame_create(terminal->window, terminal); + terminal->title = strdup("Wayland Terminal"); + window_set_title(terminal->window, terminal->title); widget_set_transparent(terminal->widget, 0); init_state_machine(&terminal->state_machine); @@ -2661,6 +2838,8 @@ terminal_create(struct display *display) terminal->display = display; terminal->margin = 5; + terminal->buffer_height = 1024; + terminal->end = 1; window_set_user_data(terminal->window, terminal); window_set_key_handler(terminal->window, key_handler); @@ -2678,6 +2857,9 @@ terminal_create(struct display *display) widget_set_button_handler(terminal->widget, button_handler); widget_set_enter_handler(terminal->widget, enter_handler); widget_set_motion_handler(terminal->widget, motion_handler); + widget_set_touch_up_handler(terminal->widget, touch_up_handler); + widget_set_touch_down_handler(terminal->widget, touch_down_handler); + widget_set_touch_motion_handler(terminal->widget, touch_motion_handler); surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); cr = cairo_create(surface); @@ -2726,6 +2908,7 @@ terminal_destroy(struct terminal *terminal) if (wl_list_empty(&terminal_list)) display_exit(terminal->display); + free(terminal->title); free(terminal); } diff --git a/clients/transformed.c b/clients/transformed.c index 54212dd4..bbc1dc02 100644 --- a/clients/transformed.c +++ b/clients/transformed.c @@ -227,8 +227,7 @@ touch_handler(struct widget *widget, struct input *input, float x, float y, void *data) { struct transformed *transformed = data; - window_touch_move(transformed->window, input, - display_get_serial(transformed->display)); + window_move(transformed->window, input, display_get_serial(transformed->display)); } static void diff --git a/clients/view.c b/clients/view.c index f5b1843f..4ac9ca50 100644 --- a/clients/view.c +++ b/clients/view.c @@ -168,7 +168,7 @@ fullscreen_handler(struct window *window, void *data) } static void -close_handler(struct window *window, void *data) +close_handler(void *data) { struct view *view = data; @@ -251,7 +251,7 @@ view_create(struct display *display, } view->window = window_create(display); - view->widget = frame_create(view->window, view); + view->widget = window_frame_create(view->window, view); window_set_title(view->window, title); g_free(title); view->display = display; diff --git a/clients/weston-info.c b/clients/weston-info.c index 5d928f54..4cc05723 100644 --- a/clients/weston-info.c +++ b/clients/weston-info.c @@ -234,8 +234,20 @@ print_shm_info(void *data) printf("\tformats:"); wl_list_for_each(format, &shm->formats, link) - printf(" %s", (format->format == WL_SHM_FORMAT_ARGB8888) ? - "ARGB8888" : "XRGB8888"); + switch (format->format) { + case WL_SHM_FORMAT_ARGB8888: + printf(" ARGB8888"); + break; + case WL_SHM_FORMAT_XRGB8888: + printf(" XRGB8888"); + break; + case WL_SHM_FORMAT_RGB565: + printf(" RGB565"); + break; + default: + printf(" unknown(%08x)", format->format); + break; + } printf("\n"); } diff --git a/clients/window.c b/clients/window.c index 56de5d78..d59b9c7f 100644 --- a/clients/window.c +++ b/clients/window.c @@ -117,6 +117,7 @@ struct display { display_output_handler_t output_configure_handler; display_global_handler_t global_handler; + display_global_handler_t global_handler_remove; void *user_data; @@ -220,7 +221,6 @@ struct surface { struct window { struct display *display; - struct window *parent; struct wl_list window_output_list; char *title; struct rectangle saved_allocation; @@ -253,7 +253,7 @@ struct window { struct surface *main_surface; struct wl_shell_surface *shell_surface; - struct frame *frame; + struct window_frame *frame; /* struct surface::link, contains also main_surface */ struct wl_list subsurface_list; @@ -285,10 +285,16 @@ struct widget { int opaque; int tooltip_count; int default_cursor; + /* If this is set to false then no cairo surface will be + * created before redrawing the surface. This is useful if the + * redraw handler is going to do completely custom rendering + * such as using EGL directly */ + int use_cairo; }; struct touch_point { int32_t id; + float x, y; struct widget *widget; struct wl_list link; }; @@ -320,6 +326,11 @@ struct input { struct wl_data_device *data_device; struct data_offer *drag_offer; struct data_offer *selection_offer; + uint32_t touch_grab; + int32_t touch_grab_id; + float drag_x, drag_y; + struct window *drag_focus; + uint32_t drag_enter_serial; struct { struct xkb_keymap *keymap; @@ -339,6 +350,7 @@ struct input { struct output { struct display *display; struct wl_output *output; + uint32_t server_output_id; struct rectangle allocation; struct wl_list link; int transform; @@ -348,51 +360,18 @@ struct output { void *user_data; }; -enum frame_button_action { - FRAME_BUTTON_NULL = 0, - FRAME_BUTTON_ICON = 1, - FRAME_BUTTON_CLOSE = 2, - FRAME_BUTTON_MINIMIZE = 3, - FRAME_BUTTON_MAXIMIZE = 4, -}; - -enum frame_button_pointer { - FRAME_BUTTON_DEFAULT = 0, - FRAME_BUTTON_OVER = 1, - FRAME_BUTTON_ACTIVE = 2, -}; - -enum frame_button_align { - FRAME_BUTTON_RIGHT = 0, - FRAME_BUTTON_LEFT = 1, -}; - -enum frame_button_decoration { - FRAME_BUTTON_NONE = 0, - FRAME_BUTTON_FANCY = 1, -}; - -struct frame_button { - struct widget *widget; - struct frame *frame; - cairo_surface_t *icon; - enum frame_button_action type; - enum frame_button_pointer state; - struct wl_list link; /* buttons_list */ - enum frame_button_align align; - enum frame_button_decoration decoration; -}; - -struct frame { +struct window_frame { struct widget *widget; struct widget *child; - struct wl_list buttons_list; + struct frame *frame; }; struct menu { struct window *window; + struct window *parent; struct widget *widget; struct input *input; + struct frame *frame; const char **entries; uint32_t time; int current; @@ -1110,6 +1089,9 @@ shm_surface_prepare(struct toysurface *base, int dx, int dy, surface->flags, leaf->resize_pool, &leaf->data); + if (!leaf->cairo_surface) + return NULL; + wl_buffer_add_listener(leaf->data->buffer, &shm_surface_buffer_listener, surface); @@ -1514,7 +1496,7 @@ window_get_output_scale(struct window *window) return scale; } -static void frame_destroy(struct frame *frame); +static void window_frame_destroy(struct window_frame *frame); static void surface_destroy(struct surface *surface) @@ -1568,7 +1550,7 @@ window_destroy(struct window *window) } if (window->frame) - frame_destroy(window->frame); + window_frame_destroy(window->frame); if (window->shell_surface) wl_shell_surface_destroy(window->shell_surface); @@ -1632,6 +1614,7 @@ widget_create(struct window *window, struct surface *surface, void *data) widget->tooltip = NULL; widget->tooltip_count = 0; widget->default_cursor = CURSOR_LEFT_PTR; + widget->use_cairo = 1; return widget; } @@ -1670,10 +1653,8 @@ widget_destroy(struct widget *widget) if (surface->widget == widget && surface->subsurface) surface_destroy(widget->surface); - if (widget->tooltip) { - free(widget->tooltip); - widget->tooltip = NULL; - } + if (widget->tooltip) + widget_destroy_tooltip(widget); wl_list_for_each(input, &display->input_list, link) { if (input->focus_widget == widget) @@ -1730,6 +1711,8 @@ widget_get_cairo_surface(struct widget *widget) struct surface *surface = widget->surface; struct window *window = widget->window; + assert(widget->use_cairo); + if (!surface->cairo_surface) { if (surface == window->main_surface) window_create_main_surface(window); @@ -1842,6 +1825,12 @@ widget_get_wl_surface(struct widget *widget) return widget->surface->surface; } +struct wl_subsurface * +widget_get_wl_subsurface(struct widget *widget) +{ + return widget->surface->subsurface; +} + uint32_t widget_get_last_time(struct widget *widget) { @@ -1956,6 +1945,13 @@ widget_schedule_redraw(struct widget *widget) window_schedule_redraw_task(widget->window); } +void +widget_set_use_cairo(struct widget *widget, + int use_cairo) +{ + widget->use_cairo = use_cairo; +} + cairo_surface_t * window_get_surface(struct window *window) { @@ -1972,12 +1968,6 @@ window_get_wl_surface(struct window *window) return window->main_surface->surface; } -struct wl_shell_surface * -window_get_wl_shell_surface(struct window *window) -{ - return window->shell_surface; -} - static void tooltip_redraw_handler(struct widget *widget, void *data) { @@ -2166,376 +2156,113 @@ static void frame_resize_handler(struct widget *widget, int32_t width, int32_t height, void *data) { - struct frame *frame = data; + struct window_frame *frame = data; struct widget *child = frame->child; - struct rectangle allocation; - struct display *display = widget->window->display; - struct surface *surface = widget->surface; - struct frame_button * button; - struct theme *t = display->theme; - int x_l, x_r, y, w, h; - int decoration_width, decoration_height; - int opaque_margin, shadow_margin; - - switch (widget->window->type) { - case TYPE_FULLSCREEN: - decoration_width = 0; - decoration_height = 0; - - allocation.x = 0; - allocation.y = 0; - allocation.width = width; - allocation.height = height; - opaque_margin = 0; - - wl_list_for_each(button, &frame->buttons_list, link) - button->widget->opaque = 1; - break; - case TYPE_MAXIMIZED: - decoration_width = t->width * 2; - decoration_height = t->width + t->titlebar_height; - - allocation.x = t->width; - allocation.y = t->titlebar_height; - allocation.width = width - decoration_width; - allocation.height = height - decoration_height; - - opaque_margin = 0; - - wl_list_for_each(button, &frame->buttons_list, link) - button->widget->opaque = 0; - break; - default: - decoration_width = (t->width + t->margin) * 2; - decoration_height = t->width + - t->titlebar_height + t->margin * 2; - - allocation.x = t->width + t->margin; - allocation.y = t->titlebar_height + t->margin; - allocation.width = width - decoration_width; - allocation.height = height - decoration_height; - - opaque_margin = t->margin + t->frame_radius; - - wl_list_for_each(button, &frame->buttons_list, link) - button->widget->opaque = 0; - break; - } - - widget_set_allocation(child, allocation.x, allocation.y, - allocation.width, allocation.height); - - if (child->resize_handler) - child->resize_handler(child, - allocation.width, - allocation.height, - child->user_data); - - width = child->allocation.width + decoration_width; - height = child->allocation.height + decoration_height; - - shadow_margin = widget->window->type == TYPE_MAXIMIZED ? 0 : t->margin; - - surface->input_region = - wl_compositor_create_region(display->compositor); - if (widget->window->type != TYPE_FULLSCREEN) { - wl_region_add(surface->input_region, - shadow_margin, shadow_margin, - width - 2 * shadow_margin, - height - 2 * shadow_margin); + struct rectangle interior; + struct rectangle input; + struct rectangle opaque; + + if (widget->window->type == TYPE_FULLSCREEN) { + interior.x = 0; + interior.y = 0; + interior.width = width; + interior.height = height; } else { - wl_region_add(surface->input_region, 0, 0, width, height); - } - - widget_set_allocation(widget, 0, 0, width, height); - - if (child->opaque) - wl_region_add(surface->opaque_region, - opaque_margin, opaque_margin, - widget->allocation.width - 2 * opaque_margin, - widget->allocation.height - 2 * opaque_margin); - - /* frame internal buttons */ - x_r = frame->widget->allocation.width - t->width - shadow_margin; - x_l = t->width + shadow_margin; - y = t->width + shadow_margin; - wl_list_for_each(button, &frame->buttons_list, link) { - const int button_padding = 4; - w = cairo_image_surface_get_width(button->icon); - h = cairo_image_surface_get_height(button->icon); - - if (button->decoration == FRAME_BUTTON_FANCY) - w += 10; - - if (button->align == FRAME_BUTTON_LEFT) { - widget_set_allocation(button->widget, - x_l, y , w + 1, h + 1); - x_l += w; - x_l += button_padding; + if (widget->window->type == TYPE_MAXIMIZED) { + frame_set_flag(frame->frame, FRAME_FLAG_MAXIMIZED); } else { - x_r -= w; - widget_set_allocation(button->widget, - x_r, y , w + 1, h + 1); - x_r -= button_padding; + frame_unset_flag(frame->frame, FRAME_FLAG_MAXIMIZED); } - } -} - -static int -frame_button_enter_handler(struct widget *widget, - struct input *input, float x, float y, void *data) -{ - struct frame_button *frame_button = data; - - widget_schedule_redraw(frame_button->widget); - frame_button->state = FRAME_BUTTON_OVER; - - return CURSOR_LEFT_PTR; -} -static void -frame_button_leave_handler(struct widget *widget, struct input *input, void *data) -{ - struct frame_button *frame_button = data; - - widget_schedule_redraw(frame_button->widget); - frame_button->state = FRAME_BUTTON_DEFAULT; -} - -static void -frame_button_button_handler(struct widget *widget, - struct input *input, uint32_t time, - uint32_t button, - enum wl_pointer_button_state state, void *data) -{ - struct frame_button *frame_button = data; - struct window *window = widget->window; - int was_pressed = (frame_button->state == FRAME_BUTTON_ACTIVE); - - if (button != BTN_LEFT) - return; - - switch (state) { - case WL_POINTER_BUTTON_STATE_PRESSED: - frame_button->state = FRAME_BUTTON_ACTIVE; - widget_schedule_redraw(frame_button->widget); - - if (frame_button->type == FRAME_BUTTON_ICON) - window_show_frame_menu(window, input, time); - return; - case WL_POINTER_BUTTON_STATE_RELEASED: - frame_button->state = FRAME_BUTTON_DEFAULT; - widget_schedule_redraw(frame_button->widget); - break; + frame_resize(frame->frame, width, height); + frame_interior(frame->frame, &interior.x, &interior.y, + &interior.width, &interior.height); } - if (!was_pressed) - return; - - switch (frame_button->type) { - case FRAME_BUTTON_CLOSE: - if (window->close_handler) - window->close_handler(window->parent, - window->user_data); - else - display_exit(window->display); - break; - case FRAME_BUTTON_MINIMIZE: - fprintf(stderr,"Minimize stub\n"); - break; - case FRAME_BUTTON_MAXIMIZE: - window_set_maximized(window, window->type != TYPE_MAXIMIZED); - break; - default: - /* Unknown operation */ - break; - } -} + widget_set_allocation(child, interior.x, interior.y, + interior.width, interior.height); -static void -frame_button_touch_down_handler(struct widget *widget, struct input *input, - uint32_t serial, uint32_t time, int32_t id, - float x, float y, void *data) -{ - struct frame_button *frame_button = data; - struct window *window = widget->window; + if (child->resize_handler) { + child->resize_handler(child, interior.width, interior.height, + child->user_data); - switch (frame_button->type) { - case FRAME_BUTTON_CLOSE: - if (window->close_handler) - window->close_handler(window->parent, - window->user_data); - else - display_exit(window->display); - break; - case FRAME_BUTTON_MINIMIZE: - fprintf(stderr,"Minimize stub\n"); - break; - case FRAME_BUTTON_MAXIMIZE: - window_set_maximized(window, window->type != TYPE_MAXIMIZED); - break; - default: - /* Unknown operation */ - break; + if (widget->window->type == TYPE_FULLSCREEN) { + width = child->allocation.width; + height = child->allocation.height; + } else { + frame_resize_inside(frame->frame, + child->allocation.width, + child->allocation.height); + width = frame_width(frame->frame); + height = frame_height(frame->frame); + } } -} - - -static int -frame_button_motion_handler(struct widget *widget, - struct input *input, uint32_t time, - float x, float y, void *data) -{ - struct frame_button *frame_button = data; - enum frame_button_pointer previous_button_state = frame_button->state; - /* only track state for a pressed button */ - if (input->grab != widget) - return CURSOR_LEFT_PTR; + widget_set_allocation(widget, 0, 0, width, height); - if (x > widget->allocation.x && - x < (widget->allocation.x + widget->allocation.width) && - y > widget->allocation.y && - y < (widget->allocation.y + widget->allocation.height)) { - frame_button->state = FRAME_BUTTON_ACTIVE; + widget->surface->input_region = + wl_compositor_create_region(widget->window->display->compositor); + if (widget->window->type != TYPE_FULLSCREEN) { + frame_input_rect(frame->frame, &input.x, &input.y, + &input.width, &input.height); + wl_region_add(widget->surface->input_region, + input.x, input.y, input.width, input.height); } else { - frame_button->state = FRAME_BUTTON_DEFAULT; + wl_region_add(widget->surface->input_region, 0, 0, width, height); } - if (frame_button->state != previous_button_state) - widget_schedule_redraw(frame_button->widget); - - return CURSOR_LEFT_PTR; -} - -static void -frame_button_redraw_handler(struct widget *widget, void *data) -{ - struct frame_button *frame_button = data; - cairo_t *cr; - int width, height, x, y; - - x = widget->allocation.x; - y = widget->allocation.y; - width = widget->allocation.width; - height = widget->allocation.height; - - if (!width) - return; - if (!height) - return; - if (widget->opaque) - return; - - cr = widget_cairo_create(widget); - - if (frame_button->decoration == FRAME_BUTTON_FANCY) { - cairo_set_line_width(cr, 1); - - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_rectangle (cr, x, y, 25, 16); + widget_set_allocation(widget, 0, 0, width, height); - cairo_stroke_preserve(cr); + if (child->opaque) { + if (widget->window->type != TYPE_FULLSCREEN) { + frame_opaque_rect(frame->frame, &opaque.x, &opaque.y, + &opaque.width, &opaque.height); - switch (frame_button->state) { - case FRAME_BUTTON_DEFAULT: - cairo_set_source_rgb(cr, 0.88, 0.88, 0.88); - break; - case FRAME_BUTTON_OVER: - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - break; - case FRAME_BUTTON_ACTIVE: - cairo_set_source_rgb(cr, 0.7, 0.7, 0.7); - break; + wl_region_add(widget->surface->opaque_region, + opaque.x, opaque.y, + opaque.width, opaque.height); + } else { + wl_region_add(widget->surface->opaque_region, + 0, 0, width, height); } - - cairo_fill (cr); - - x += 4; } - cairo_set_source_surface(cr, frame_button->icon, x, y); - cairo_paint(cr); - - cairo_destroy(cr); -} - -static struct widget * -frame_button_create(struct frame *frame, void *data, enum frame_button_action type, - enum frame_button_align align, enum frame_button_decoration style) -{ - struct frame_button *frame_button; - const char *icon = data; - - frame_button = xzalloc (sizeof *frame_button); - frame_button->icon = cairo_image_surface_create_from_png(icon); - frame_button->widget = widget_add_widget(frame->widget, frame_button); - frame_button->frame = frame; - frame_button->type = type; - frame_button->align = align; - frame_button->decoration = style; - - wl_list_insert(frame->buttons_list.prev, &frame_button->link); - - widget_set_redraw_handler(frame_button->widget, frame_button_redraw_handler); - widget_set_enter_handler(frame_button->widget, frame_button_enter_handler); - widget_set_leave_handler(frame_button->widget, frame_button_leave_handler); - widget_set_touch_down_handler(frame_button->widget, frame_button_touch_down_handler); - widget_set_button_handler(frame_button->widget, frame_button_button_handler); - widget_set_motion_handler(frame_button->widget, frame_button_motion_handler); - return frame_button->widget; -} - -static void -frame_button_destroy(struct frame_button *frame_button) -{ - widget_destroy(frame_button->widget); - wl_list_remove(&frame_button->link); - cairo_surface_destroy(frame_button->icon); - free(frame_button); - return; + widget_schedule_redraw(widget); } static void frame_redraw_handler(struct widget *widget, void *data) { cairo_t *cr; + struct window_frame *frame = data; struct window *window = widget->window; - struct theme *t = window->display->theme; - uint32_t flags = 0; if (window->type == TYPE_FULLSCREEN) return; + if (window->focus_count) { + frame_set_flag(frame->frame, FRAME_FLAG_ACTIVE); + } else { + frame_unset_flag(frame->frame, FRAME_FLAG_ACTIVE); + } + cr = widget_cairo_create(widget); - if (window->focus_count) - flags |= THEME_FRAME_ACTIVE; - if (window->type == TYPE_MAXIMIZED) - flags |= THEME_FRAME_MAXIMIZED; - theme_render_frame(t, cr, widget->allocation.width, - widget->allocation.height, window->title, flags); + frame_repaint(frame->frame, cr); cairo_destroy(cr); } static int -frame_get_pointer_image_for_location(struct frame *frame, struct input *input) +frame_get_pointer_image_for_location(struct window_frame *frame, + enum theme_location location) { - struct theme *t = frame->widget->window->display->theme; struct window *window = frame->widget->window; - int location; if (window->type != TYPE_TOPLEVEL) return CURSOR_LEFT_PTR; - location = theme_get_location(t, input->sx, input->sy, - frame->widget->allocation.width, - frame->widget->allocation.height, - window->type == TYPE_MAXIMIZED ? - THEME_FRAME_MAXIMIZED : 0); - switch (location) { case THEME_LOCATION_RESIZING_TOP: return CURSOR_TOP; @@ -2561,15 +2288,15 @@ frame_get_pointer_image_for_location(struct frame *frame, struct input *input) } static void -frame_menu_func(struct window *window, int index, void *data) +frame_menu_func(struct window *window, + struct input *input, int index, void *data) { struct display *display; switch (index) { case 0: /* close */ if (window->close_handler) - window->close_handler(window->parent, - window->user_data); + window->close_handler(window->user_data); else display_exit(window->display); break; @@ -2624,7 +2351,14 @@ static int frame_enter_handler(struct widget *widget, struct input *input, float x, float y, void *data) { - return frame_get_pointer_image_for_location(data, input); + struct window_frame *frame = data; + enum theme_location location; + + location = frame_pointer_enter(frame->frame, input, x, y); + if (frame_status(frame->frame) & FRAME_STATUS_REPAINT) + widget_schedule_redraw(frame->widget); + + return frame_get_pointer_image_for_location(data, location); } static int @@ -2632,7 +2366,79 @@ frame_motion_handler(struct widget *widget, struct input *input, uint32_t time, float x, float y, void *data) { - return frame_get_pointer_image_for_location(data, input); + struct window_frame *frame = data; + enum theme_location location; + + location = frame_pointer_motion(frame->frame, input, x, y); + if (frame_status(frame->frame) & FRAME_STATUS_REPAINT) + widget_schedule_redraw(frame->widget); + + return frame_get_pointer_image_for_location(data, location); +} + +static void +frame_leave_handler(struct widget *widget, + struct input *input, void *data) +{ + struct window_frame *frame = data; + + frame_pointer_leave(frame->frame, input); + if (frame_status(frame->frame) & FRAME_STATUS_REPAINT) + widget_schedule_redraw(frame->widget); +} + +static void +frame_handle_status(struct window_frame *frame, struct input *input, + uint32_t time, enum theme_location location) +{ + struct window *window = frame->widget->window; + uint32_t status; + + status = frame_status(frame->frame); + if (status & FRAME_STATUS_REPAINT) + widget_schedule_redraw(frame->widget); + + if (status & FRAME_STATUS_MINIMIZE) + fprintf(stderr,"Minimize stub\n"); + + if (status & FRAME_STATUS_MENU) { + window_show_frame_menu(window, input, time); + frame_status_clear(frame->frame, FRAME_STATUS_MENU); + } + + if (status & FRAME_STATUS_MAXIMIZE) { + window_set_maximized(window, window->type != TYPE_MAXIMIZED); + frame_status_clear(frame->frame, FRAME_STATUS_MAXIMIZE); + } + + if (status & FRAME_STATUS_CLOSE) { + if (window->close_handler) + window->close_handler(window->user_data); + else + display_exit(window->display); + return; + } + + if ((status & FRAME_STATUS_MOVE) && window->shell_surface) { + input_ungrab(input); + wl_shell_surface_move(window->shell_surface, + input_get_seat(input), + window->display->serial); + + frame_status_clear(frame->frame, FRAME_STATUS_MOVE); + } + + if ((status & FRAME_STATUS_RESIZE) && window->shell_surface) { + input_ungrab(input); + + window->resizing = 1; + wl_shell_surface_resize(window->shell_surface, + input_get_seat(input), + window->display->serial, + location); + + frame_status_clear(frame->frame, FRAME_STATUS_RESIZE); + } } static void @@ -2642,99 +2448,62 @@ frame_button_handler(struct widget *widget, void *data) { - struct frame *frame = data; - struct window *window = widget->window; - struct display *display = window->display; - int location; - - if (state != WL_POINTER_BUTTON_STATE_PRESSED) - return; + struct window_frame *frame = data; + enum theme_location location; - location = theme_get_location(display->theme, input->sx, input->sy, - frame->widget->allocation.width, - frame->widget->allocation.height, - window->type == TYPE_MAXIMIZED ? - THEME_FRAME_MAXIMIZED : 0); - - if (window->display->shell && button == BTN_LEFT && - window->type == TYPE_TOPLEVEL) { - switch (location) { - case THEME_LOCATION_TITLEBAR: - if (!window->shell_surface) - break; - input_ungrab(input); - wl_shell_surface_move(window->shell_surface, - input_get_seat(input), - display->serial); - break; - case THEME_LOCATION_RESIZING_TOP: - case THEME_LOCATION_RESIZING_BOTTOM: - case THEME_LOCATION_RESIZING_LEFT: - case THEME_LOCATION_RESIZING_RIGHT: - case THEME_LOCATION_RESIZING_TOP_LEFT: - case THEME_LOCATION_RESIZING_TOP_RIGHT: - case THEME_LOCATION_RESIZING_BOTTOM_LEFT: - case THEME_LOCATION_RESIZING_BOTTOM_RIGHT: - if (!window->shell_surface) - break; - input_ungrab(input); - - window->resizing = 1; - wl_shell_surface_resize(window->shell_surface, - input_get_seat(input), - display->serial, location); - break; - } - } else if (button == BTN_RIGHT && - (window->type == TYPE_TOPLEVEL || - window->type == TYPE_MAXIMIZED)) { - window_show_frame_menu(window, input, time); - } + location = frame_pointer_button(frame->frame, input, button, state); + frame_handle_status(frame, input, time, location); } -static void +static void frame_touch_down_handler(struct widget *widget, struct input *input, uint32_t serial, uint32_t time, int32_t id, float x, float y, void *data) { - struct window *window = widget->window; - struct display *display = window->display; - - wl_shell_surface_move(window->shell_surface, - input_get_seat(input), - display->serial); + struct window_frame *frame = data; + + frame_touch_down(frame->frame, input, id, x, y); + frame_handle_status(frame, input, time, THEME_LOCATION_CLIENT_AREA); +} + +static void +frame_touch_up_handler(struct widget *widget, + struct input *input, uint32_t serial, uint32_t time, + int32_t id, void *data) +{ + struct window_frame *frame = data; + + frame_touch_up(frame->frame, input, id); + frame_handle_status(frame, input, time, THEME_LOCATION_CLIENT_AREA); } struct widget * -frame_create(struct window *window, void *data) +window_frame_create(struct window *window, void *data) { - struct frame *frame; + struct window_frame *frame; + uint32_t buttons; + + if (window->type == TYPE_CUSTOM) { + buttons = FRAME_BUTTON_NONE; + } else { + buttons = FRAME_BUTTON_ALL; + } frame = xzalloc(sizeof *frame); + frame->frame = frame_create(window->display->theme, 0, 0, + buttons, window->title); + frame->widget = window_add_widget(window, frame); frame->child = widget_add_widget(frame->widget, data); widget_set_redraw_handler(frame->widget, frame_redraw_handler); widget_set_resize_handler(frame->widget, frame_resize_handler); widget_set_enter_handler(frame->widget, frame_enter_handler); + widget_set_leave_handler(frame->widget, frame_leave_handler); widget_set_motion_handler(frame->widget, frame_motion_handler); widget_set_button_handler(frame->widget, frame_button_handler); widget_set_touch_down_handler(frame->widget, frame_touch_down_handler); - - /* Create empty list for frame buttons */ - wl_list_init(&frame->buttons_list); - - frame_button_create(frame, DATADIR "/weston/icon_window.png", - FRAME_BUTTON_ICON, FRAME_BUTTON_LEFT, FRAME_BUTTON_NONE); - - frame_button_create(frame, DATADIR "/weston/sign_close.png", - FRAME_BUTTON_CLOSE, FRAME_BUTTON_RIGHT, FRAME_BUTTON_FANCY); - - frame_button_create(frame, DATADIR "/weston/sign_maximize.png", - FRAME_BUTTON_MAXIMIZE, FRAME_BUTTON_RIGHT, FRAME_BUTTON_FANCY); - - frame_button_create(frame, DATADIR "/weston/sign_minimize.png", - FRAME_BUTTON_MINIMIZE, FRAME_BUTTON_RIGHT, FRAME_BUTTON_FANCY); + widget_set_touch_up_handler(frame->widget, frame_touch_up_handler); window->frame = frame; @@ -2742,7 +2511,8 @@ frame_create(struct window *window, void *data) } void -frame_set_child_size(struct widget *widget, int child_width, int child_height) +window_frame_set_child_size(struct widget *widget, int child_width, + int child_height) { struct display *display = widget->window->display; struct theme *t = display->theme; @@ -2766,12 +2536,9 @@ frame_set_child_size(struct widget *widget, int child_width, int child_height) } static void -frame_destroy(struct frame *frame) +window_frame_destroy(struct window_frame *frame) { - struct frame_button *button, *tmp; - - wl_list_for_each_safe(button, tmp, &frame->buttons_list, link) - frame_button_destroy(button); + frame_destroy(frame->frame); /* frame->child must be destroyed by the application */ widget_destroy(frame->widget); @@ -2813,6 +2580,31 @@ input_set_focus_widget(struct input *input, struct widget *focus, } } +void +touch_grab(struct input *input, int32_t touch_id) +{ + input->touch_grab = 1; + input->touch_grab_id = touch_id; +} + +void +touch_ungrab(struct input *input) +{ + struct touch_point *tp, *tmp; + + input->touch_grab = 0; + + wl_list_for_each_safe(tp, tmp, + &input->touch_point_list, link) { + if (tp->id != input->touch_grab_id) + continue; + wl_list_remove(&tp->link); + free(tp); + + return; + } +} + void input_grab(struct input *input, struct widget *widget, uint32_t button) { @@ -3038,6 +2830,8 @@ keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { struct input *input = data; + struct xkb_keymap *keymap; + struct xkb_state *state; char *map_str; if (!data) { @@ -3056,26 +2850,30 @@ keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, return; } - input->xkb.keymap = xkb_map_new_from_string(input->display->xkb_context, - map_str, - XKB_KEYMAP_FORMAT_TEXT_V1, - 0); + keymap = xkb_map_new_from_string(input->display->xkb_context, + map_str, + XKB_KEYMAP_FORMAT_TEXT_V1, + 0); munmap(map_str, size); close(fd); - if (!input->xkb.keymap) { + if (!keymap) { fprintf(stderr, "failed to compile keymap\n"); return; } - input->xkb.state = xkb_state_new(input->xkb.keymap); - if (!input->xkb.state) { + state = xkb_state_new(keymap); + if (!state) { fprintf(stderr, "failed to create XKB state\n"); - xkb_map_unref(input->xkb.keymap); - input->xkb.keymap = NULL; + xkb_map_unref(keymap); return; } + xkb_keymap_unref(input->xkb.keymap); + xkb_state_unref(input->xkb.state); + input->xkb.keymap = keymap; + input->xkb.state = state; + input->xkb.control_mask = 1 << xkb_map_mod_get_index(input->xkb.keymap, "Control"); input->xkb.alt_mask = @@ -3148,8 +2946,7 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, input->modifiers == MOD_ALT_MASK && state == WL_KEYBOARD_KEY_STATE_PRESSED) { if (window->close_handler) - window->close_handler(window->parent, - window->user_data); + window->close_handler(window->user_data); else display_exit(window->display); } else if (window->key_handler) { @@ -3236,6 +3033,8 @@ touch_handle_down(void *data, struct wl_touch *wl_touch, if (tp) { tp->id = id; tp->widget = widget; + tp->x = sx; + tp->y = sy; wl_list_insert(&input->touch_point_list, &tp->link); if (widget->touch_down_handler) @@ -3295,6 +3094,8 @@ touch_handle_motion(void *data, struct wl_touch *wl_touch, if (tp->id != id) continue; + tp->x = sx; + tp->y = sy; if (tp->widget->touch_motion_handler) (*tp->widget->touch_motion_handler)(tp->widget, input, time, id, sx, sy, @@ -3412,6 +3213,23 @@ input_get_position(struct input *input, int32_t *x, int32_t *y) *y = input->sy; } +int +input_get_touch(struct input *input, int32_t id, float *x, float *y) +{ + struct touch_point *tp; + + wl_list_for_each(tp, &input->touch_point_list, link) { + if (tp->id != id) + continue; + + *x = tp->x; + *y = tp->y; + return 0; + } + + return -1; +} + struct display * input_get_display(struct input *input) { @@ -3508,9 +3326,14 @@ data_device_enter(void *data, struct wl_data_device *data_device, float y = wl_fixed_to_double(y_w); char **p; - input->pointer_enter_serial = serial; window = wl_surface_get_user_data(surface); - input->pointer_focus = window; + input->drag_enter_serial = serial; + input->drag_focus = window, + input->drag_x = x; + input->drag_y = y; + + if (!input->touch_grab) + input->pointer_enter_serial = serial; if (offer) { input->drag_offer = wl_data_offer_get_user_data(offer); @@ -3524,7 +3347,6 @@ data_device_enter(void *data, struct wl_data_device *data_device, types_data = NULL; } - window = input->pointer_focus; if (window->data_handler) window->data_handler(window, input, x, y, types_data, window->user_data); @@ -3546,13 +3368,13 @@ data_device_motion(void *data, struct wl_data_device *data_device, uint32_t time, wl_fixed_t x_w, wl_fixed_t y_w) { struct input *input = data; - struct window *window = input->pointer_focus; + struct window *window = input->drag_focus; float x = wl_fixed_to_double(x_w); float y = wl_fixed_to_double(y_w); void *types_data; - input->sx = x; - input->sy = y; + input->drag_x = x; + input->drag_y = y; if (input->drag_offer) types_data = input->drag_offer->types.data; @@ -3568,11 +3390,18 @@ static void data_device_drop(void *data, struct wl_data_device *data_device) { struct input *input = data; - struct window *window = input->pointer_focus; + struct window *window = input->drag_focus; + float x, y; + + x = input->drag_x; + y = input->drag_y; if (window->drop_handler) window->drop_handler(window, input, - input->sx, input->sy, window->user_data); + x, y, window->user_data); + + if (input->touch_grab) + touch_ungrab(input); } static void @@ -3741,7 +3570,7 @@ void input_accept(struct input *input, const char *type) { wl_data_offer_accept(input->drag_offer->offer, - input->pointer_enter_serial, type); + input->drag_enter_serial, type); } static void @@ -3789,8 +3618,8 @@ input_receive_drag_data(struct input *input, const char *mime_type, data_func_t func, void *data) { data_offer_receive_data(input->drag_offer, mime_type, func, data); - input->drag_offer->x = input->sx; - input->drag_offer->y = input->sy; + input->drag_offer->x = input->drag_x; + input->drag_offer->y = input->drag_y; } int @@ -3844,16 +3673,6 @@ window_move(struct window *window, struct input *input, uint32_t serial) wl_shell_surface_move(window->shell_surface, input->seat, serial); } -void -window_touch_move(struct window *window, struct input *input, uint32_t serial) -{ - if (!window->shell_surface) - return; - - wl_shell_surface_move(window->shell_surface, input->seat, - window->display->serial); -} - static void surface_set_synchronized(struct surface *surface) { @@ -3956,21 +3775,10 @@ hack_prevent_EGL_sub_surface_deadlock(struct window *window) } static void -idle_resize(struct window *window) +window_do_resize(struct window *window) { struct surface *surface; - window->resize_needed = 0; - window->redraw_needed = 1; - - DBG("from %dx%d to %dx%d\n", - window->main_surface->server_allocation.width, - window->main_surface->server_allocation.height, - window->pending_allocation.width, - window->pending_allocation.height); - - hack_prevent_EGL_sub_surface_deadlock(window); - widget_set_allocation(window->main_surface->widget, window->pending_allocation.x, window->pending_allocation.y, @@ -3993,6 +3801,46 @@ idle_resize(struct window *window) } } +static void +idle_resize(struct window *window) +{ + window->resize_needed = 0; + window->redraw_needed = 1; + + DBG("from %dx%d to %dx%d\n", + window->main_surface->server_allocation.width, + window->main_surface->server_allocation.height, + window->pending_allocation.width, + window->pending_allocation.height); + + hack_prevent_EGL_sub_surface_deadlock(window); + + window_do_resize(window); +} + +static void +undo_resize(struct window *window) +{ + window->pending_allocation.width = + window->main_surface->server_allocation.width; + window->pending_allocation.height = + window->main_surface->server_allocation.height; + + DBG("back to %dx%d\n", + window->main_surface->server_allocation.width, + window->main_surface->server_allocation.height); + + window_do_resize(window); + + if (window->pending_allocation.width == 0 && + window->pending_allocation.height == 0) { + fprintf(stderr, "Error: Could not draw a surface, " + "most likely due to insufficient disk space in " + "%s (XDG_RUNTIME_DIR).\n", getenv("XDG_RUNTIME_DIR")); + exit(EXIT_FAILURE); + } +} + void window_schedule_resize(struct window *window, int width, int height) { @@ -4052,6 +3900,7 @@ menu_destroy(struct menu *menu) { widget_destroy(menu->widget); window_destroy(menu->window); + frame_destroy(menu->frame); free(menu); } @@ -4116,25 +3965,31 @@ static const struct wl_callback_listener listener = { frame_callback }; -static void +static int surface_redraw(struct surface *surface) { DBG_OBJ(surface->surface, "begin\n"); if (!surface->window->redraw_needed && !surface->redraw_needed) - return; + return 0; /* Whole-window redraw forces a redraw even if the previous has * not yet hit the screen. */ if (surface->frame_cb) { if (!surface->window->redraw_needed) - return; + return 0; DBG_OBJ(surface->frame_cb, "cancelled\n"); wl_callback_destroy(surface->frame_cb); } + if (surface->widget->use_cairo && + !widget_get_cairo_surface(surface->widget)) { + DBG_OBJ(surface->surface, "cancelled due buffer failure\n"); + return -1; + } + surface->frame_cb = wl_surface_frame(surface->surface); wl_callback_add_listener(surface->frame_cb, &listener, surface); DBG_OBJ(surface->frame_cb, "new\n"); @@ -4143,6 +3998,7 @@ surface_redraw(struct surface *surface) DBG_OBJ(surface->surface, "-> widget_redraw\n"); widget_redraw(surface->widget); DBG_OBJ(surface->surface, "done\n"); + return 0; } static void @@ -4150,6 +4006,8 @@ idle_redraw(struct task *task, uint32_t events) { struct window *window = container_of(task, struct window, redraw_task); struct surface *surface; + int failed = 0; + int resized = 0; DBG(" --------- \n"); @@ -4164,16 +4022,35 @@ idle_redraw(struct task *task, uint32_t events) } idle_resize(window); + resized = 1; } - wl_list_for_each(surface, &window->subsurface_list, link) - surface_redraw(surface); + if (surface_redraw(window->main_surface) < 0) { + /* + * Only main_surface failure will cause us to undo the resize. + * If sub-surfaces fail, they will just be broken with old + * content. + */ + failed = 1; + } else { + wl_list_for_each(surface, &window->subsurface_list, link) { + if (surface == window->main_surface) + continue; + + surface_redraw(surface); + } + } window->redraw_needed = 0; window_flush(window); wl_list_for_each(surface, &window->subsurface_list, link) surface_set_synchronized_default(surface); + + if (resized && failed) { + /* Restore widget tree to correspond to what is on screen. */ + undo_resize(window); + } } static void @@ -4207,6 +4084,12 @@ window_is_fullscreen(struct window *window) return window->type == TYPE_FULLSCREEN; } +int +window_is_transient(struct window *window) +{ + return window->type == TYPE_TRANSIENT; +} + static void configure_request_completed(void *data, struct wl_callback *callback, uint32_t time) { @@ -4375,6 +4258,10 @@ window_set_title(struct window *window, const char *title) { free(window->title); window->title = strdup(title); + if (window->frame) { + frame_set_title(window->frame->frame, title); + widget_schedule_redraw(window->frame->widget); + } if (window->shell_surface) wl_shell_surface_set_title(window->shell_surface, title); } @@ -4489,8 +4376,7 @@ surface_create(struct window *window) } static struct window * -window_create_internal(struct display *display, - struct window *parent, int type) +window_create_internal(struct display *display, int type) { struct window *window; struct surface *surface; @@ -4498,7 +4384,6 @@ window_create_internal(struct display *display, window = xzalloc(sizeof *window); wl_list_init(&window->subsurface_list); window->display = display; - window->parent = parent; surface = surface_create(window); window->main_surface = surface; @@ -4542,13 +4427,13 @@ window_create_internal(struct display *display, struct window * window_create(struct display *display) { - return window_create_internal(display, NULL, TYPE_NONE); + return window_create_internal(display, TYPE_NONE); } struct window * window_create_custom(struct display *display) { - return window_create_internal(display, NULL, TYPE_CUSTOM); + return window_create_internal(display, TYPE_CUSTOM); } struct window * @@ -4557,8 +4442,7 @@ window_create_transient(struct display *display, struct window *parent, { struct window *window; - window = window_create_internal(parent->display, - parent, TYPE_TRANSIENT); + window = window_create_internal(parent->display, TYPE_TRANSIENT); window->x = x; window->y = y; @@ -4566,7 +4450,7 @@ window_create_transient(struct display *display, struct window *parent, if (display->shell) wl_shell_surface_set_transient( window->shell_surface, - window->parent->main_surface->surface, + parent->main_surface->surface, window->x, window->y, flags); return window; @@ -4575,9 +4459,11 @@ window_create_transient(struct display *display, struct window *parent, static void menu_set_item(struct menu *menu, int sy) { + int32_t x, y, width, height; int next; - next = (sy - 8) / 20; + frame_interior(menu->frame, &x, &y, &width, &height); + next = (sy - y) / 20; if (menu->current != next) { menu->current = next; widget_schedule_redraw(menu->widget); @@ -4631,8 +4517,8 @@ menu_button_handler(struct widget *widget, (menu->release_count > 0 || time - menu->time > 500)) { /* Either relase after press-drag-release or * click-motion-click. */ - menu->func(menu->window->parent, - menu->current, menu->window->parent->user_data); + menu->func(menu->parent, input, + menu->current, menu->parent->user_data); input_ungrab(input); menu_destroy(menu); } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) { @@ -4644,35 +4530,35 @@ static void menu_redraw_handler(struct widget *widget, void *data) { cairo_t *cr; - const int32_t r = 3, margin = 3; struct menu *menu = data; - int32_t width, height, i; + int32_t x, y, width, height, i; cr = widget_cairo_create(widget); - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); - cairo_paint(cr); - width = widget->allocation.width; - height = widget->allocation.height; - rounded_rect(cr, 0, 0, width, height, r); + frame_repaint(menu->frame, cr); + frame_interior(menu->frame, &x, &y, &width, &height); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_set_source_rgba(cr, 0.0, 0.0, 0.4, 0.8); + theme_set_background_source(menu->window->display->theme, + cr, THEME_FRAME_ACTIVE); + cairo_rectangle(cr, x, y, width, height); cairo_fill(cr); + cairo_select_font_face(cr, "sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 12); + for (i = 0; i < menu->count; i++) { if (i == menu->current) { cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_rectangle(cr, margin, i * 20 + margin, - width - 2 * margin, 20); + cairo_rectangle(cr, x, y + i * 20, width, 20); cairo_fill(cr); cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_move_to(cr, 10, i * 20 + 16); + cairo_move_to(cr, x + 10, y + i * 20 + 16); cairo_show_text(cr, menu->entries[i]); } else { - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_move_to(cr, 10, i * 20 + 16); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_move_to(cr, x + 10, y + i * 20 + 16); cairo_show_text(cr, menu->entries[i]); } } @@ -4688,22 +4574,25 @@ window_show_menu(struct display *display, { struct window *window; struct menu *menu; - const int32_t margin = 3; + int32_t ix, iy; menu = malloc(sizeof *menu); if (!menu) return; - window = window_create_internal(parent->display, parent, TYPE_MENU); + window = window_create_internal(parent->display, TYPE_MENU); if (!window) { free(menu); return; } menu->window = window; + menu->parent = parent; menu->widget = window_add_widget(menu->window, menu); window_set_buffer_scale (menu->window, window_get_buffer_scale (parent)); window_set_buffer_transform (menu->window, window_get_buffer_transform (parent)); + menu->frame = frame_create(window->display->theme, 0, 0, + FRAME_BUTTON_NONE, NULL); menu->entries = entries; menu->count = count; menu->release_count = 0; @@ -4716,10 +4605,6 @@ window_show_menu(struct display *display, window->y = y; input_ungrab(input); - wl_shell_surface_set_popup(window->shell_surface, input->seat, - display_get_serial(window->display), - window->parent->main_surface->surface, - window->x, window->y, 0); widget_set_redraw_handler(menu->widget, menu_redraw_handler); widget_set_enter_handler(menu->widget, menu_enter_handler); @@ -4728,7 +4613,16 @@ window_show_menu(struct display *display, widget_set_button_handler(menu->widget, menu_button_handler); input_grab(input, menu->widget, 0); - window_schedule_resize(window, 200, count * 20 + margin * 2); + frame_resize_inside(menu->frame, 200, count * 20); + frame_set_flag(menu->frame, FRAME_FLAG_ACTIVE); + window_schedule_resize(window, frame_width(menu->frame), + frame_height(menu->frame)); + + frame_interior(menu->frame, &ix, &iy, NULL, NULL); + wl_shell_surface_set_popup(window->shell_surface, input->seat, + display_get_serial(window->display), + parent->main_surface->surface, + window->x - ix, window->y - iy, 0); } void @@ -4849,6 +4743,7 @@ display_add_output(struct display *d, uint32_t id) output->scale = 1; output->output = wl_registry_bind(d->registry, id, &wl_output_interface, 2); + output->server_output_id = id; wl_list_insert(d->output_list.prev, &output->link); wl_output_add_listener(output->output, &output_listener, output); @@ -4865,6 +4760,19 @@ output_destroy(struct output *output) free(output); } +static void +display_destroy_output(struct display *d, uint32_t id) +{ + struct output *output; + + wl_list_for_each(output, &d->output_list, link) { + if (output->server_output_id == id) { + output_destroy(output); + break; + } + } +} + void display_set_global_handler(struct display *display, display_global_handler_t handler) @@ -4881,6 +4789,15 @@ display_set_global_handler(struct display *display, global->version, display->user_data); } +void +display_set_global_handler_remove(struct display *display, + display_global_handler_t remove_handler) +{ + display->global_handler_remove = remove_handler; + if (!remove_handler) + return; +} + void display_set_output_configure_handler(struct display *display, display_output_handler_t handler) @@ -4965,7 +4882,7 @@ fini_xkb(struct input *input) xkb_map_unref(input->xkb.keymap); } -#define MAX(a,b) ((a) > (b) ? a : b) +#define MIN(a,b) ((a) < (b) ? a : b) static void display_add_input(struct display *d, uint32_t id) @@ -4975,7 +4892,7 @@ display_add_input(struct display *d, uint32_t id) input = xzalloc(sizeof *input); input->display = d; input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, - MAX(d->seat_version, 3)); + MIN(d->seat_version, 3)); input->touch_focus = NULL; input->pointer_focus = NULL; input->keyboard_focus = NULL; @@ -5115,9 +5032,15 @@ registry_handle_global_remove(void *data, struct wl_registry *registry, if (global->name != name) continue; - /* XXX: Should destroy bound globals, and call - * the counterpart of display::global_handler - */ + if (strcmp(global->interface, "wl_output") == 0) + display_destroy_output(d, name); + + /* XXX: Should destroy remaining bound globals */ + + if (d->global_handler_remove) + d->global_handler_remove(d, name, global->interface, + global->version, d->user_data); + wl_list_remove(&global->link); free(global->interface); free(global); @@ -5480,12 +5403,6 @@ display_get_argb_egl_config(struct display *d) return d->argb_config; } -struct wl_shell * -display_get_shell(struct display *display) -{ - return display->shell; -} - int display_acquire_window_surface(struct display *display, struct window *window, @@ -5656,3 +5573,9 @@ xstrdup(const char *s) { return fail_on_null(strdup(s)); } + +void * +xrealloc(char *p, size_t s) +{ + return fail_on_null(realloc(p, s)); +} diff --git a/clients/window.h b/clients/window.h index 4427ab5a..dec133fc 100644 --- a/clients/window.h +++ b/clients/window.h @@ -28,7 +28,6 @@ #include #include "../shared/config-parser.h" #include "../shared/zalloc.h" -#include "subsurface-client-protocol.h" #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) @@ -62,6 +61,8 @@ void * xzalloc(size_t s); char * xstrdup(const char *s); +void * +xrealloc(char *p, size_t s); struct display * display_create(int *argc, char *argv[]); @@ -87,9 +88,6 @@ display_get_cairo_device(struct display *display); struct wl_compositor * display_get_compositor(struct display *display); -struct wl_shell * -display_get_shell(struct display *display); - struct output * display_get_output(struct display *display); @@ -104,6 +102,9 @@ typedef void (*display_global_handler_t)(struct display *display, void display_set_global_handler(struct display *display, display_global_handler_t handler); +void +display_set_global_handler_remove(struct display *display, + display_global_handler_t remove_handler); void * display_bind(struct display *display, uint32_t name, const struct wl_interface *interface, uint32_t version); @@ -211,7 +212,7 @@ typedef void (*window_drop_handler_t)(struct window *window, struct input *input, int32_t x, int32_t y, void *data); -typedef void (*window_close_handler_t)(struct window *window, void *data); +typedef void (*window_close_handler_t)(void *data); typedef void (*window_fullscreen_handler_t)(struct window *window, void *data); typedef void (*window_output_handler_t)(struct window *window, struct output *output, @@ -277,7 +278,8 @@ window_create_custom(struct display *display); int window_has_focus(struct window *window); -typedef void (*menu_func_t)(struct window *window, int index, void *data); +typedef void (*menu_func_t)(struct window *window, + struct input *input, int index, void *data); void window_show_menu(struct display *display, @@ -329,8 +331,6 @@ window_get_display(struct window *window); void window_move(struct window *window, struct input *input, uint32_t time); void -window_touch_move(struct window *window, struct input *input, uint32_t time); -void window_get_allocation(struct window *window, struct rectangle *allocation); void window_schedule_redraw(struct window *window); @@ -347,8 +347,8 @@ window_get_surface(struct window *window); struct wl_surface * window_get_wl_surface(struct window *window); -struct wl_shell_surface * -window_get_wl_shell_surface(struct window *window); +struct wl_subsurface * +widget_get_wl_subsurface(struct widget *widget); enum window_buffer_type { WINDOW_BUFFER_TYPE_EGL_WINDOW, @@ -362,6 +362,9 @@ display_surface_damage(struct display *display, cairo_surface_t *cairo_surface, void window_set_buffer_type(struct window *window, enum window_buffer_type type); +int +window_is_transient(struct window *window); + int window_is_fullscreen(struct window *window); @@ -506,12 +509,15 @@ widget_set_axis_handler(struct widget *widget, widget_axis_handler_t handler); void widget_schedule_redraw(struct widget *widget); +void +widget_set_use_cairo(struct widget *widget, int use_cairo); struct widget * -frame_create(struct window *window, void *data); +window_frame_create(struct window *window, void *data); void -frame_set_child_size(struct widget *widget, int child_width, int child_height); +window_frame_set_child_size(struct widget *widget, int child_width, + int child_height); void input_set_pointer_image(struct input *input, int pointer); @@ -519,6 +525,9 @@ input_set_pointer_image(struct input *input, int pointer); void input_get_position(struct input *input, int32_t *x, int32_t *y); +int +input_get_touch(struct input *input, int32_t id, float *x, float *y); + #define MOD_SHIFT_MASK 0x01 #define MOD_ALT_MASK 0x02 #define MOD_CONTROL_MASK 0x04 @@ -526,6 +535,12 @@ input_get_position(struct input *input, int32_t *x, int32_t *y); uint32_t input_get_modifiers(struct input *input); +void +touch_grab(struct input *input, int32_t touch_id); + +void +touch_ungrab(struct input *input); + void input_grab(struct input *input, struct widget *widget, uint32_t button); diff --git a/clients/wscreensaver.c b/clients/wscreensaver.c index 9a2c47ad..47f6c8a3 100644 --- a/clients/wscreensaver.c +++ b/clients/wscreensaver.c @@ -200,7 +200,7 @@ create_wscreensaver_instance(struct wscreensaver *screensaver, window_get_wl_surface(mi->window), output); } else { - mi->widget = frame_create(mi->window, mi); + mi->widget = window_frame_create(mi->window, mi); } widget_set_redraw_handler(mi->widget, redraw_handler); diff --git a/configure.ac b/configure.ac index 50bce23a..571bf601 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ m4_define([weston_major_version], [1]) m4_define([weston_minor_version], [3]) -m4_define([weston_micro_version], [1]) +m4_define([weston_micro_version], [91]) m4_define([weston_version], [weston_major_version.weston_minor_version.weston_micro_version]) @@ -35,6 +35,8 @@ LT_INIT([disable-static]) AC_ARG_VAR([WESTON_NATIVE_BACKEND], [Set the native backend to use, if Weston is not running under Wayland nor X11. @<:@default=drm-backend.so@:>@]) +AC_ARG_VAR([WESTON_SHELL_CLIENT], + [Set the default desktop shell client to load if none is specified in weston.ini. @<:@default=weston-desktop-shell@:>@]) PKG_PROG_PKG_CONFIG() @@ -53,16 +55,17 @@ AC_CHECK_DECL(CLOCK_MONOTONIC,[], [[#include ]]) AC_CHECK_HEADERS([execinfo.h]) -AC_CHECK_FUNCS([mkostemp strchrnul initgroups]) +AC_CHECK_FUNCS([mkostemp strchrnul initgroups posix_fallocate]) -COMPOSITOR_MODULES="wayland-server >= 1.2.91 pixman-1" +COMPOSITOR_MODULES="wayland-server >= 1.3.90 pixman-1" AC_ARG_ENABLE(egl, [ --disable-egl],, enable_egl=yes) AM_CONDITIONAL(ENABLE_EGL, test x$enable_egl = xyes) if test x$enable_egl = xyes; then AC_DEFINE([ENABLE_EGL], [1], [Build Weston with EGL support]) - COMPOSITOR_MODULES="$COMPOSITOR_MODULES egl >= 7.10 glesv2" + PKG_CHECK_MODULES(EGL, [egl >= 7.10 glesv2]) + PKG_CHECK_MODULES([EGL_TESTS], [egl >= 7.10 glesv2 wayland-client wayland-egl]) fi AC_ARG_ENABLE(xkbcommon, @@ -78,8 +81,6 @@ if test x$enable_xkbcommon = xyes; then COMPOSITOR_MODULES="$COMPOSITOR_MODULES xkbcommon >= 0.3.0" fi -PKG_CHECK_MODULES(COMPOSITOR, [$COMPOSITOR_MODULES]) - AC_ARG_ENABLE(setuid-install, [ --enable-setuid-install],, enable_setuid_install=yes) AM_CONDITIONAL(ENABLE_SETUID_INSTALL, test x$enable_setuid_install = xyes) @@ -104,6 +105,8 @@ if test x$enable_xwayland = xyes; then fi fi +PKG_CHECK_MODULES(LIBDRM, [libdrm], + [AC_DEFINE(HAVE_LIBDRM, 1, [Define if libdrm is available]) have_libdrm=yes], have_libdrm=no) AC_ARG_ENABLE(x11-compositor, [ --enable-x11-compositor],, enable_x11_compositor=yes) @@ -143,12 +146,13 @@ fi AC_ARG_ENABLE(drm-compositor, [ --enable-drm-compositor],, enable_drm_compositor=yes) -AM_CONDITIONAL(ENABLE_DRM_COMPOSITOR, test x$enable_drm_compositor = xyes -a x$enable_egl = xyes) -if test x$enable_drm_compositor = xyes -a x$enable_egl = xyes; then +AM_CONDITIONAL(ENABLE_DRM_COMPOSITOR, test x$enable_drm_compositor = xyes) +if test x$enable_drm_compositor = xyes; then AC_DEFINE([BUILD_DRM_COMPOSITOR], [1], [Build the DRM compositor]) PKG_CHECK_MODULES(DRM_COMPOSITOR, [libudev >= 136 libdrm >= 2.4.30 gbm mtdev >= 1.1.0]) fi +PKG_CHECK_MODULES(COMPOSITOR, [$COMPOSITOR_MODULES]) AC_ARG_ENABLE(wayland-compositor, [ --enable-wayland-compositor],, enable_wayland_compositor=yes) @@ -157,7 +161,7 @@ AM_CONDITIONAL(ENABLE_WAYLAND_COMPOSITOR, if test x$enable_wayland_compositor = xyes -a x$enable_egl = xyes; then AC_DEFINE([BUILD_WAYLAND_COMPOSITOR], [1], [Build the Wayland (nested) compositor]) - PKG_CHECK_MODULES(WAYLAND_COMPOSITOR, [wayland-client wayland-egl]) + PKG_CHECK_MODULES(WAYLAND_COMPOSITOR, [wayland-client wayland-egl wayland-cursor]) fi @@ -264,6 +268,8 @@ AC_SUBST(JPEG_LIBS) PKG_CHECK_MODULES(CAIRO, [cairo]) +PKG_CHECK_MODULES(TEST_CLIENT, [wayland-client]) + AC_ARG_ENABLE(simple-clients, AS_HELP_STRING([--disable-simple-clients], [do not build the simple wl_shm clients]),, @@ -314,20 +320,26 @@ AC_ARG_ENABLE(resize-optimization, AS_IF([test "x$enable_resize_optimization" = "xyes"], [AC_DEFINE([USE_RESIZE_POOL], [1], [Use resize memory pool as a performance optimization])]) +PKG_CHECK_MODULES(SYSTEMD_LOGIN, [libsystemd-login], + [have_systemd_login=yes], [have_systemd_login=no]) +AS_IF([test "x$have_systemd_login" = "xyes"], + [AC_DEFINE([HAVE_SYSTEMD_LOGIN], [1], [Have systemd-login])]) +AM_CONDITIONAL(HAVE_SYSTEMD_LOGIN, test "x$have_systemd_login" = "xyes") + +PKG_CHECK_MODULES(SYSTEMD_LOGIN_209, [libsystemd-login >= 209], + [have_systemd_login_209=yes], [have_systemd_login_209=no]) +AS_IF([test "x$have_systemd_login_209" = "xyes"], + [AC_DEFINE([HAVE_SYSTEMD_LOGIN_209], [1], [Have systemd-login >= 209])]) + AC_ARG_ENABLE(weston-launch, [ --enable-weston-launch],, enable_weston_launch=yes) AM_CONDITIONAL(BUILD_WESTON_LAUNCH, test x$enable_weston_launch == xyes) if test x$enable_weston_launch == xyes; then - PKG_CHECK_MODULES(WESTON_LAUNCH, [libdrm]) - PKG_CHECK_MODULES(SYSTEMD_LOGIN, [libsystemd-login], - [have_systemd_login=yes], [have_systemd_login=no]) - AS_IF([test "x$have_systemd_login" = "xyes"], - [AC_DEFINE([HAVE_SYSTEMD_LOGIN], [1], [Have systemd-login])]) - AC_CHECK_LIB([pam], [pam_open_session], [have_pam=yes], [have_pam=no]) if test x$have_pam == xno; then AC_ERROR([weston-launch requires pam]) fi - WESTON_LAUNCH_LIBS="$WESTON_LAUNCH_LIBS -lpam" + PAM_LIBS=-lpam + AC_SUBST(PAM_LIBS) fi if test x$enable_egl = xyes; then @@ -351,13 +363,6 @@ AM_CONDITIONAL(BUILD_SUBSURFACES_CLIENT, AM_CONDITIONAL(ENABLE_DESKTOP_SHELL, true) -AC_ARG_ENABLE(tablet-shell, - AS_HELP_STRING([--disable-tablet-shell], - [do not build tablet-shell server plugin and client]),, - enable_tablet_shell=yes) -AM_CONDITIONAL(ENABLE_TABLET_SHELL, - test "x$enable_tablet_shell" = "xyes") - # CMS modules AC_ARG_ENABLE(colord, AS_HELP_STRING([--disable-colord], @@ -377,6 +382,28 @@ if test "x$enable_colord" != "xno"; then fi AM_CONDITIONAL(ENABLE_COLORD, test "x$enable_colord" = "xyes") +# dbus support +AC_ARG_ENABLE(dbus, + AS_HELP_STRING([--disable-dbus], + [do not build with dbus support]),, + enable_dbus=auto) +if test "x$enable_dbus" != "xno"; then + PKG_CHECK_MODULES(DBUS, + dbus-1 >= 1.6, + have_dbus=yes, + have_dbus=no) + if test "x$have_dbus" = "xno" -a "x$enable_dbus" = "xyes"; then + AC_MSG_ERROR([dbus support explicitly requested, but libdbus couldn't be found]) + fi + if test "x$have_dbus" = "xyes"; then + enable_dbus=yes + AC_DEFINE([HAVE_DBUS], [1], [Build with dbus support]) + else + enable_dbus=no + fi +fi +AM_CONDITIONAL(ENABLE_DBUS, test "x$enable_dbus" = "xyes") + AC_ARG_ENABLE(wcap-tools, [ --disable-wcap-tools],, enable_wcap_tools=yes) AM_CONDITIONAL(BUILD_WCAP_TOOLS, test x$enable_wcap_tools = xyes) if test x$enable_wcap_tools = xyes; then @@ -425,11 +452,18 @@ AC_MSG_NOTICE([Weston's native backend: $WESTON_NATIVE_BACKEND]) AC_DEFINE_UNQUOTED([WESTON_NATIVE_BACKEND], ["$WESTON_NATIVE_BACKEND"], [The default backend to load, if not wayland nor x11.]) -AC_ARG_ENABLE(demo-clients, - AS_HELP_STRING([--enable-demo-clients], - [install demo clients built with weston]),, - enable_demo_clients=no) -AM_CONDITIONAL(ENABLE_DEMO_CLIENTS, [test "x$enable_demo_clients" = "xyes"]) +if test "x$WESTON_SHELL_CLIENT" = "x"; then + WESTON_SHELL_CLIENT="weston-desktop-shell" +fi +AC_MSG_NOTICE([Weston's default desktop shell client: $WESTON_SHELL_CLIENT]) +AC_DEFINE_UNQUOTED([WESTON_SHELL_CLIENT], ["$WESTON_SHELL_CLIENT"], + [The default desktop shell client to load.]) + +AC_ARG_ENABLE(demo-clients-install, + AS_HELP_STRING([--enable-demo-clients-install], + [Install demo clients built with weston]),, + enable_demo_clients_install=no) +AM_CONDITIONAL(INSTALL_DEMO_CLIENTS, [test "x$enable_demo_clients_install" = "xyes"]) PKG_CHECK_MODULES(LCMS, lcms2, [have_lcms=yes], [have_lcms=no]) @@ -443,10 +477,22 @@ if test x$wayland_scanner = x; then AC_MSG_ERROR([wayland-scanner is needed to compile weston]) fi +PKG_CHECK_MODULES(WAYLAND_SCANNER, wayland-scanner) +AC_PATH_PROG(XMLLINT, xmllint) +AC_ARG_WITH([dtddir], + AS_HELP_STRING([--with-dtddir], + [Directory containing the Wayland + protocol DTD @<:@default=from pkgconfig@:>@]), + [dtddir="$withval"], + [dtddir=$($PKG_CONFIG --variable=pkgdatadir wayland-scanner)]) +AC_SUBST([dtddir]) +AM_CONDITIONAL([HAVE_XMLLINT], [test "x$XMLLINT" != "x" -a "x$dtddir" != "x"]) + AC_CONFIG_FILES([Makefile shared/Makefile src/Makefile - src/xwayland/Makefile + xwayland/Makefile + desktop-shell/Makefile src/version.h src/weston.pc clients/Makefile @@ -464,13 +510,14 @@ AC_MSG_RESULT([ Cairo Renderer ${with_cairo} EGL ${enable_egl} libxkbcommon ${enable_xkbcommon} + xcb_xkb ${have_xcb_xkb} XWayland ${enable_xwayland} + dbus ${enable_dbus} Build wcap utility ${enable_wcap_tools} - Build Tablet Shell ${enable_tablet_shell} weston-launch utility ${enable_weston_launch} - weston-launch systemd support ${have_systemd_login} + systemd-login support ${have_systemd_login} DRM Compositor ${enable_drm_compositor} X11 Compositor ${enable_x11_compositor} @@ -487,7 +534,7 @@ AC_MSG_RESULT([ Build Simple Clients ${enable_simple_clients} Build Simple EGL Clients ${enable_simple_egl_clients} - Install Demo Clients ${enable_demo_clients} + Install Demo Clients ${enable_demo_clients_install} Colord Support ${have_colord} GLU Support ${have_glu} diff --git a/desktop-shell/.gitignore b/desktop-shell/.gitignore new file mode 100644 index 00000000..ff42e5fe --- /dev/null +++ b/desktop-shell/.gitignore @@ -0,0 +1,4 @@ +desktop-shell-protocol.c +desktop-shell-server-protocol.h +xdg-shell-protocol.c +xdg-shell-server-protocol.h diff --git a/desktop-shell/Makefile.am b/desktop-shell/Makefile.am new file mode 100644 index 00000000..fef85f2e --- /dev/null +++ b/desktop-shell/Makefile.am @@ -0,0 +1,39 @@ +moduledir = $(libdir)/weston +module_LTLIBRARIES = $(desktop_shell) + +AM_CPPFLAGS = \ + -I$(top_srcdir)/shared \ + -I$(top_srcdir)/src \ + -I$(top_builddir)/src \ + -DDATADIR='"$(datadir)"' \ + -DMODULEDIR='"$(moduledir)"' \ + -DLIBEXECDIR='"$(libexecdir)"' \ + -DIN_WESTON + +if ENABLE_DESKTOP_SHELL +desktop_shell = desktop-shell.la +desktop_shell_la_LDFLAGS = -module -avoid-version +desktop_shell_la_LIBADD = $(COMPOSITOR_LIBS) \ + ../shared/libshared.la +desktop_shell_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) +desktop_shell_la_SOURCES = \ + shell.h \ + shell.c \ + exposay.c \ + input-panel.c \ + desktop-shell-protocol.c \ + desktop-shell-server-protocol.h \ + xdg-shell-protocol.c \ + xdg-shell-server-protocol.h +endif + +BUILT_SOURCES = \ + desktop-shell-protocol.c \ + desktop-shell-server-protocol.h \ + xdg-shell-protocol.c \ + xdg-shell-server-protocol.h + +CLEANFILES = $(BUILT_SOURCES) + +wayland_protocoldir = $(top_srcdir)/protocol +include $(top_srcdir)/wayland-scanner.mk diff --git a/desktop-shell/exposay.c b/desktop-shell/exposay.c new file mode 100644 index 00000000..01bf0b13 --- /dev/null +++ b/desktop-shell/exposay.c @@ -0,0 +1,609 @@ +/* + * Copyright © 2010-2012 Intel Corporation + * Copyright © 2011-2012 Collabora, Ltd. + * Copyright © 2013 Raspberry Pi Foundation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include + +#include "shell.h" + +struct exposay_surface { + struct desktop_shell *shell; + struct weston_surface *surface; + struct weston_view *view; + struct wl_list link; + + int x; + int y; + int width; + int height; + double scale; + + int row; + int column; + + /* The animations only apply a transformation for their own lifetime, + * and don't have an option to indefinitely maintain the + * transformation in a steady state - so, we apply our own once the + * animation has finished. */ + struct weston_transform transform; +}; + +static void exposay_set_state(struct desktop_shell *shell, + enum exposay_target_state state, + struct weston_seat *seat); +static void exposay_check_state(struct desktop_shell *shell); + +static void +exposay_in_flight_inc(struct desktop_shell *shell) +{ + shell->exposay.in_flight++; +} + +static void +exposay_in_flight_dec(struct desktop_shell *shell) +{ + if (--shell->exposay.in_flight > 0) + return; + + exposay_check_state(shell); +} + +static void +exposay_animate_in_done(struct weston_view_animation *animation, void *data) +{ + struct exposay_surface *esurface = data; + + wl_list_insert(&esurface->view->geometry.transformation_list, + &esurface->transform.link); + weston_matrix_init(&esurface->transform.matrix); + weston_matrix_scale(&esurface->transform.matrix, + esurface->scale, esurface->scale, 1.0f); + weston_matrix_translate(&esurface->transform.matrix, + esurface->x - esurface->view->geometry.x, + esurface->y - esurface->view->geometry.y, + 0); + + weston_view_geometry_dirty(esurface->view); + weston_compositor_schedule_repaint(esurface->view->surface->compositor); + + exposay_in_flight_dec(esurface->shell); +} + +static void +exposay_animate_in(struct exposay_surface *esurface) +{ + exposay_in_flight_inc(esurface->shell); + + weston_move_scale_run(esurface->view, + esurface->x - esurface->view->geometry.x, + esurface->y - esurface->view->geometry.y, + 1.0, esurface->scale, 0, + exposay_animate_in_done, esurface); +} + +static void +exposay_animate_out_done(struct weston_view_animation *animation, void *data) +{ + struct exposay_surface *esurface = data; + struct desktop_shell *shell = esurface->shell; + + wl_list_remove(&esurface->link); + free(esurface); + + exposay_in_flight_dec(shell); +} + +static void +exposay_animate_out(struct exposay_surface *esurface) +{ + exposay_in_flight_inc(esurface->shell); + + /* Remove the static transformation set up by + * exposay_transform_in_done(). */ + wl_list_remove(&esurface->transform.link); + weston_view_geometry_dirty(esurface->view); + + weston_move_scale_run(esurface->view, + esurface->x - esurface->view->geometry.x, + esurface->y - esurface->view->geometry.y, + 1.0, esurface->scale, 1, + exposay_animate_out_done, esurface); +} + +static void +exposay_highlight_surface(struct desktop_shell *shell, + struct exposay_surface *esurface) +{ + struct weston_view *view = NULL; + + if (esurface) { + shell->exposay.row_current = esurface->row; + shell->exposay.column_current = esurface->column; + view = esurface->view; + } + + activate(shell, view->surface, shell->exposay.seat); + shell->exposay.focus_current = view; +} + +static int +exposay_is_animating(struct desktop_shell *shell) +{ + if (shell->exposay.state_cur == EXPOSAY_LAYOUT_INACTIVE || + shell->exposay.state_cur == EXPOSAY_LAYOUT_OVERVIEW) + return 0; + + return (shell->exposay.in_flight > 0); +} + +static void +exposay_pick(struct desktop_shell *shell, int x, int y) +{ + struct exposay_surface *esurface; + + if (exposay_is_animating(shell)) + return; + + wl_list_for_each(esurface, &shell->exposay.surface_list, link) { + if (x < esurface->x || x > esurface->x + esurface->width) + continue; + if (y < esurface->y || y > esurface->y + esurface->height) + continue; + + exposay_highlight_surface(shell, esurface); + return; + } +} + +/* Pretty lame layout for now; just tries to make a square. Should take + * aspect ratio into account really. Also needs to be notified of surface + * addition and removal and adjust layout/animate accordingly. */ +static enum exposay_layout_state +exposay_layout(struct desktop_shell *shell) +{ + struct workspace *workspace = shell->exposay.workspace; + struct weston_compositor *compositor = shell->compositor; + struct weston_output *output = get_default_output(compositor); + struct weston_view *view; + struct exposay_surface *esurface, *highlight = NULL; + int w, h; + int i; + int last_row_removed = 0; + + wl_list_init(&shell->exposay.surface_list); + + shell->exposay.num_surfaces = 0; + wl_list_for_each(view, &workspace->layer.view_list, layer_link) { + if (!get_shell_surface(view->surface)) + continue; + shell->exposay.num_surfaces++; + } + + if (shell->exposay.num_surfaces == 0) { + shell->exposay.grid_size = 0; + shell->exposay.hpadding_outer = 0; + shell->exposay.vpadding_outer = 0; + shell->exposay.padding_inner = 0; + shell->exposay.surface_size = 0; + return EXPOSAY_LAYOUT_OVERVIEW; + } + + /* Lay the grid out as square as possible, losing surfaces from the + * bottom row if required. Start with fixed padding of a 10% margin + * around the outside and 80px internal padding between surfaces, and + * maximise the area made available to surfaces after this, but only + * to a maximum of 1/3rd the total output size. + * + * If we can't make a square grid, add one extra row at the bottom + * which will have a smaller number of columns. + * + * XXX: Surely there has to be a better way to express this maths, + * right?! + */ + shell->exposay.grid_size = floor(sqrtf(shell->exposay.num_surfaces)); + if (pow(shell->exposay.grid_size, 2) != shell->exposay.num_surfaces) + shell->exposay.grid_size++; + last_row_removed = pow(shell->exposay.grid_size, 2) - shell->exposay.num_surfaces; + + shell->exposay.hpadding_outer = (output->width / 10); + shell->exposay.vpadding_outer = (output->height / 10); + shell->exposay.padding_inner = 80; + + w = output->width - (shell->exposay.hpadding_outer * 2); + w -= shell->exposay.padding_inner * (shell->exposay.grid_size - 1); + w /= shell->exposay.grid_size; + + h = output->height - (shell->exposay.vpadding_outer * 2); + h -= shell->exposay.padding_inner * (shell->exposay.grid_size - 1); + h /= shell->exposay.grid_size; + + shell->exposay.surface_size = (w < h) ? w : h; + if (shell->exposay.surface_size > (output->width / 2)) + shell->exposay.surface_size = output->width / 2; + if (shell->exposay.surface_size > (output->height / 2)) + shell->exposay.surface_size = output->height / 2; + + i = 0; + wl_list_for_each(view, &workspace->layer.view_list, layer_link) { + int pad; + + pad = shell->exposay.surface_size + shell->exposay.padding_inner; + + if (!get_shell_surface(view->surface)) + continue; + + esurface = malloc(sizeof(*esurface)); + if (!esurface) { + exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, + shell->exposay.seat); + break; + } + + wl_list_insert(&shell->exposay.surface_list, &esurface->link); + esurface->shell = shell; + esurface->view = view; + + esurface->row = i / shell->exposay.grid_size; + esurface->column = i % shell->exposay.grid_size; + + esurface->x = shell->exposay.hpadding_outer; + esurface->x += pad * esurface->column; + esurface->y = shell->exposay.vpadding_outer; + esurface->y += pad * esurface->row; + + if (esurface->row == shell->exposay.grid_size - 1) + esurface->x += (shell->exposay.surface_size + shell->exposay.padding_inner) * last_row_removed / 2; + + if (view->surface->width > view->surface->height) + esurface->scale = shell->exposay.surface_size / (float) view->surface->width; + else + esurface->scale = shell->exposay.surface_size / (float) view->surface->height; + esurface->width = view->surface->width * esurface->scale; + esurface->height = view->surface->height * esurface->scale; + + if (shell->exposay.focus_current == esurface->view) + highlight = esurface; + + set_alpha_if_fullscreen(get_shell_surface(view->surface)); + + exposay_animate_in(esurface); + + i++; + } + + if (highlight) + exposay_highlight_surface(shell, highlight); + + weston_compositor_schedule_repaint(shell->compositor); + + return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW; +} + +static void +exposay_focus(struct weston_pointer_grab *grab) +{ +} + +static void +exposay_motion(struct weston_pointer_grab *grab, uint32_t time, + wl_fixed_t x, wl_fixed_t y) +{ + struct desktop_shell *shell = + container_of(grab, struct desktop_shell, exposay.grab_ptr); + + weston_pointer_move(grab->pointer, x, y); + + exposay_pick(shell, + wl_fixed_to_int(grab->pointer->x), + wl_fixed_to_int(grab->pointer->y)); +} + +static void +exposay_button(struct weston_pointer_grab *grab, uint32_t time, uint32_t button, + uint32_t state_w) +{ + struct desktop_shell *shell = + container_of(grab, struct desktop_shell, exposay.grab_ptr); + struct weston_seat *seat = grab->pointer->seat; + enum wl_pointer_button_state state = state_w; + + if (button != BTN_LEFT) + return; + + /* Store the surface we clicked on, and don't do anything if we end up + * releasing on a different surface. */ + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + shell->exposay.clicked = shell->exposay.focus_current; + return; + } + + if (shell->exposay.focus_current == shell->exposay.clicked) + exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat); + else + shell->exposay.clicked = NULL; +} + +static void +exposay_pointer_grab_cancel(struct weston_pointer_grab *grab) +{ + struct desktop_shell *shell = + container_of(grab, struct desktop_shell, exposay.grab_ptr); + + exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat); +} + +static const struct weston_pointer_grab_interface exposay_ptr_grab = { + exposay_focus, + exposay_motion, + exposay_button, + exposay_pointer_grab_cancel, +}; + +static int +exposay_maybe_move(struct desktop_shell *shell, int row, int column) +{ + struct exposay_surface *esurface; + + wl_list_for_each(esurface, &shell->exposay.surface_list, link) { + if (esurface->row != row || esurface->column != column) + continue; + + exposay_highlight_surface(shell, esurface); + return 1; + } + + return 0; +} + +static void +exposay_key(struct weston_keyboard_grab *grab, uint32_t time, uint32_t key, + uint32_t state_w) +{ + struct weston_seat *seat = grab->keyboard->seat; + struct desktop_shell *shell = + container_of(grab, struct desktop_shell, exposay.grab_kbd); + enum wl_keyboard_key_state state = state_w; + + if (state != WL_KEYBOARD_KEY_STATE_RELEASED) + return; + + switch (key) { + case KEY_ESC: + exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat); + break; + case KEY_ENTER: + exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat); + break; + case KEY_UP: + exposay_maybe_move(shell, shell->exposay.row_current - 1, + shell->exposay.column_current); + break; + case KEY_DOWN: + /* Special case for trying to move to the bottom row when it + * has fewer items than all the others. */ + if (!exposay_maybe_move(shell, shell->exposay.row_current + 1, + shell->exposay.column_current) && + shell->exposay.row_current < (shell->exposay.grid_size - 1)) { + exposay_maybe_move(shell, shell->exposay.row_current + 1, + (shell->exposay.num_surfaces % + shell->exposay.grid_size) - 1); + } + break; + case KEY_LEFT: + exposay_maybe_move(shell, shell->exposay.row_current, + shell->exposay.column_current - 1); + break; + case KEY_RIGHT: + exposay_maybe_move(shell, shell->exposay.row_current, + shell->exposay.column_current + 1); + break; + case KEY_TAB: + /* Try to move right, then down (and to the leftmost column), + * then if all else fails, to the top left. */ + if (!exposay_maybe_move(shell, shell->exposay.row_current, + shell->exposay.column_current + 1) && + !exposay_maybe_move(shell, shell->exposay.row_current + 1, 0)) + exposay_maybe_move(shell, 0, 0); + break; + default: + break; + } +} + +static void +exposay_modifier(struct weston_keyboard_grab *grab, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ + struct desktop_shell *shell = + container_of(grab, struct desktop_shell, exposay.grab_kbd); + struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat; + + /* We want to know when mod has been pressed and released. + * FIXME: There is a problem here: if mod is pressed, then a key + * is pressed and released, then mod is released, we will treat that + * as if only mod had been pressed and released. */ + if (seat->modifier_state) { + if (seat->modifier_state == shell->binding_modifier) { + shell->exposay.mod_pressed = true; + } else { + shell->exposay.mod_invalid = true; + } + } else { + if (shell->exposay.mod_pressed && !shell->exposay.mod_invalid) + exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat); + + shell->exposay.mod_invalid = false; + shell->exposay.mod_pressed = false; + } + + return; +} + +static void +exposay_cancel(struct weston_keyboard_grab *grab) +{ + struct desktop_shell *shell = + container_of(grab, struct desktop_shell, exposay.grab_kbd); + + exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat); +} + +static const struct weston_keyboard_grab_interface exposay_kbd_grab = { + exposay_key, + exposay_modifier, + exposay_cancel, +}; + +/** + * Called when the transition from overview -> inactive has completed. + */ +static enum exposay_layout_state +exposay_set_inactive(struct desktop_shell *shell) +{ + struct weston_seat *seat = shell->exposay.seat; + + weston_keyboard_end_grab(seat->keyboard); + weston_pointer_end_grab(seat->pointer); + if (seat->keyboard->input_method_resource) + seat->keyboard->grab = &seat->keyboard->input_method_grab; + + return EXPOSAY_LAYOUT_INACTIVE; +} + +/** + * Begins the transition from overview to inactive. */ +static enum exposay_layout_state +exposay_transition_inactive(struct desktop_shell *shell, int switch_focus) +{ + struct exposay_surface *esurface; + + /* Call activate() before we start the animations to avoid + * animating back the old state and then immediately transitioning + * to the new. */ + if (switch_focus && shell->exposay.focus_current) + activate(shell, shell->exposay.focus_current->surface, + shell->exposay.seat); + else if (shell->exposay.focus_prev) + activate(shell, shell->exposay.focus_prev->surface, + shell->exposay.seat); + + wl_list_for_each(esurface, &shell->exposay.surface_list, link) + exposay_animate_out(esurface); + weston_compositor_schedule_repaint(shell->compositor); + + return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE; +} + +static enum exposay_layout_state +exposay_transition_active(struct desktop_shell *shell) +{ + struct weston_seat *seat = shell->exposay.seat; + + shell->exposay.workspace = get_current_workspace(shell); + shell->exposay.focus_prev = get_default_view (seat->keyboard->focus); + shell->exposay.focus_current = get_default_view (seat->keyboard->focus); + shell->exposay.clicked = NULL; + wl_list_init(&shell->exposay.surface_list); + + lower_fullscreen_layer(shell); + shell->exposay.grab_kbd.interface = &exposay_kbd_grab; + weston_keyboard_start_grab(seat->keyboard, + &shell->exposay.grab_kbd); + weston_keyboard_set_focus(seat->keyboard, NULL); + + shell->exposay.grab_ptr.interface = &exposay_ptr_grab; + weston_pointer_start_grab(seat->pointer, + &shell->exposay.grab_ptr); + weston_pointer_set_focus(seat->pointer, NULL, + seat->pointer->x, seat->pointer->y); + + return exposay_layout(shell); +} + +static void +exposay_check_state(struct desktop_shell *shell) +{ + enum exposay_layout_state state_new = shell->exposay.state_cur; + int do_switch = 0; + + /* Don't do anything whilst animations are running, just store up + * target state changes and only act on them when the animations have + * completed. */ + if (exposay_is_animating(shell)) + return; + + switch (shell->exposay.state_target) { + case EXPOSAY_TARGET_OVERVIEW: + switch (shell->exposay.state_cur) { + case EXPOSAY_LAYOUT_OVERVIEW: + goto out; + case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW: + state_new = EXPOSAY_LAYOUT_OVERVIEW; + break; + default: + state_new = exposay_transition_active(shell); + break; + } + break; + + case EXPOSAY_TARGET_SWITCH: + do_switch = 1; /* fallthrough */ + case EXPOSAY_TARGET_CANCEL: + switch (shell->exposay.state_cur) { + case EXPOSAY_LAYOUT_INACTIVE: + goto out; + case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE: + state_new = exposay_set_inactive(shell); + break; + default: + state_new = exposay_transition_inactive(shell, do_switch); + break; + } + + break; + } + +out: + shell->exposay.state_cur = state_new; +} + +static void +exposay_set_state(struct desktop_shell *shell, enum exposay_target_state state, + struct weston_seat *seat) +{ + shell->exposay.state_target = state; + shell->exposay.seat = seat; + exposay_check_state(shell); +} + +void +exposay_binding(struct weston_seat *seat, enum weston_keyboard_modifier modifier, + void *data) +{ + struct desktop_shell *shell = data; + + exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, seat); +} diff --git a/desktop-shell/input-panel.c b/desktop-shell/input-panel.c new file mode 100644 index 00000000..267b3ef0 --- /dev/null +++ b/desktop-shell/input-panel.c @@ -0,0 +1,354 @@ +/* + * Copyright © 2010-2012 Intel Corporation + * Copyright © 2011-2012 Collabora, Ltd. + * Copyright © 2013 Raspberry Pi Foundation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include + +#include "shell.h" +#include "desktop-shell-server-protocol.h" +#include "input-method-server-protocol.h" + +struct input_panel_surface { + struct wl_resource *resource; + struct wl_signal destroy_signal; + + struct desktop_shell *shell; + + struct wl_list link; + struct weston_surface *surface; + struct weston_view *view; + struct wl_listener surface_destroy_listener; + + struct weston_output *output; + uint32_t panel; +}; + +static void +show_input_panels(struct wl_listener *listener, void *data) +{ + struct desktop_shell *shell = + container_of(listener, struct desktop_shell, + show_input_panel_listener); + struct input_panel_surface *ipsurf, *next; + + shell->text_input.surface = (struct weston_surface*)data; + + if (shell->showing_input_panels) + return; + + shell->showing_input_panels = true; + + if (!shell->locked) + wl_list_insert(&shell->panel_layer.link, + &shell->input_panel_layer.link); + + wl_list_for_each_safe(ipsurf, next, + &shell->input_panel.surfaces, link) { + if (ipsurf->surface->width == 0) + continue; + wl_list_insert(&shell->input_panel_layer.view_list, + &ipsurf->view->layer_link); + weston_view_geometry_dirty(ipsurf->view); + weston_view_update_transform(ipsurf->view); + weston_surface_damage(ipsurf->surface); + weston_slide_run(ipsurf->view, ipsurf->surface->height * 0.9, + 0, NULL, NULL); + } +} + +static void +hide_input_panels(struct wl_listener *listener, void *data) +{ + struct desktop_shell *shell = + container_of(listener, struct desktop_shell, + hide_input_panel_listener); + struct weston_view *view, *next; + + if (!shell->showing_input_panels) + return; + + shell->showing_input_panels = false; + + if (!shell->locked) + wl_list_remove(&shell->input_panel_layer.link); + + wl_list_for_each_safe(view, next, + &shell->input_panel_layer.view_list, layer_link) + weston_view_unmap(view); +} + +static void +update_input_panels(struct wl_listener *listener, void *data) +{ + struct desktop_shell *shell = + container_of(listener, struct desktop_shell, + update_input_panel_listener); + + memcpy(&shell->text_input.cursor_rectangle, data, sizeof(pixman_box32_t)); +} + +static void +input_panel_configure(struct weston_surface *surface, int32_t sx, int32_t sy) +{ + struct input_panel_surface *ip_surface = surface->configure_private; + struct desktop_shell *shell = ip_surface->shell; + float x, y; + + if (surface->width == 0) + return; + + if (ip_surface->panel) { + x = get_default_view(shell->text_input.surface)->geometry.x + shell->text_input.cursor_rectangle.x2; + y = get_default_view(shell->text_input.surface)->geometry.y + shell->text_input.cursor_rectangle.y2; + } else { + x = ip_surface->output->x + (ip_surface->output->width - surface->width) / 2; + y = ip_surface->output->y + ip_surface->output->height - surface->height; + } + + weston_view_set_position(ip_surface->view, x, y); + + if (!weston_surface_is_mapped(surface) && shell->showing_input_panels) { + wl_list_insert(&shell->input_panel_layer.view_list, + &ip_surface->view->layer_link); + weston_view_update_transform(ip_surface->view); + weston_surface_damage(surface); + weston_slide_run(ip_surface->view, ip_surface->view->surface->height * 0.9, 0, NULL, NULL); + } +} + +static void +destroy_input_panel_surface(struct input_panel_surface *input_panel_surface) +{ + wl_signal_emit(&input_panel_surface->destroy_signal, input_panel_surface); + + wl_list_remove(&input_panel_surface->surface_destroy_listener.link); + wl_list_remove(&input_panel_surface->link); + + input_panel_surface->surface->configure = NULL; + weston_view_destroy(input_panel_surface->view); + + free(input_panel_surface); +} + +static struct input_panel_surface * +get_input_panel_surface(struct weston_surface *surface) +{ + if (surface->configure == input_panel_configure) { + return surface->configure_private; + } else { + return NULL; + } +} + +static void +input_panel_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct input_panel_surface *ipsurface = container_of(listener, + struct input_panel_surface, + surface_destroy_listener); + + if (ipsurface->resource) { + wl_resource_destroy(ipsurface->resource); + } else { + destroy_input_panel_surface(ipsurface); + } +} + +static struct input_panel_surface * +create_input_panel_surface(struct desktop_shell *shell, + struct weston_surface *surface) +{ + struct input_panel_surface *input_panel_surface; + + input_panel_surface = calloc(1, sizeof *input_panel_surface); + if (!input_panel_surface) + return NULL; + + surface->configure = input_panel_configure; + surface->configure_private = input_panel_surface; + + input_panel_surface->shell = shell; + + input_panel_surface->surface = surface; + input_panel_surface->view = weston_view_create(surface); + + wl_signal_init(&input_panel_surface->destroy_signal); + input_panel_surface->surface_destroy_listener.notify = input_panel_handle_surface_destroy; + wl_signal_add(&surface->destroy_signal, + &input_panel_surface->surface_destroy_listener); + + wl_list_init(&input_panel_surface->link); + + return input_panel_surface; +} + +static void +input_panel_surface_set_toplevel(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output_resource, + uint32_t position) +{ + struct input_panel_surface *input_panel_surface = + wl_resource_get_user_data(resource); + struct desktop_shell *shell = input_panel_surface->shell; + + wl_list_insert(&shell->input_panel.surfaces, + &input_panel_surface->link); + + input_panel_surface->output = wl_resource_get_user_data(output_resource); + input_panel_surface->panel = 0; +} + +static void +input_panel_surface_set_overlay_panel(struct wl_client *client, + struct wl_resource *resource) +{ + struct input_panel_surface *input_panel_surface = + wl_resource_get_user_data(resource); + struct desktop_shell *shell = input_panel_surface->shell; + + wl_list_insert(&shell->input_panel.surfaces, + &input_panel_surface->link); + + input_panel_surface->panel = 1; +} + +static const struct wl_input_panel_surface_interface input_panel_surface_implementation = { + input_panel_surface_set_toplevel, + input_panel_surface_set_overlay_panel +}; + +static void +destroy_input_panel_surface_resource(struct wl_resource *resource) +{ + struct input_panel_surface *ipsurf = + wl_resource_get_user_data(resource); + + destroy_input_panel_surface(ipsurf); +} + +static void +input_panel_get_input_panel_surface(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface_resource) +{ + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + struct desktop_shell *shell = wl_resource_get_user_data(resource); + struct input_panel_surface *ipsurf; + + if (get_input_panel_surface(surface)) { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "wl_input_panel::get_input_panel_surface already requested"); + return; + } + + ipsurf = create_input_panel_surface(shell, surface); + if (!ipsurf) { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "surface->configure already set"); + return; + } + + ipsurf->resource = + wl_resource_create(client, + &wl_input_panel_surface_interface, 1, id); + wl_resource_set_implementation(ipsurf->resource, + &input_panel_surface_implementation, + ipsurf, + destroy_input_panel_surface_resource); +} + +static const struct wl_input_panel_interface input_panel_implementation = { + input_panel_get_input_panel_surface +}; + +static void +unbind_input_panel(struct wl_resource *resource) +{ + struct desktop_shell *shell = wl_resource_get_user_data(resource); + + shell->input_panel.binding = NULL; +} + +static void +bind_input_panel(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct desktop_shell *shell = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, + &wl_input_panel_interface, 1, id); + + if (shell->input_panel.binding == NULL) { + wl_resource_set_implementation(resource, + &input_panel_implementation, + shell, unbind_input_panel); + shell->input_panel.binding = resource; + return; + } + + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "interface object already bound"); + wl_resource_destroy(resource); +} + +void +input_panel_destroy(struct desktop_shell *shell) +{ + wl_list_remove(&shell->show_input_panel_listener.link); + wl_list_remove(&shell->hide_input_panel_listener.link); +} + +int +input_panel_setup(struct desktop_shell *shell) +{ + struct weston_compositor *ec = shell->compositor; + + shell->show_input_panel_listener.notify = show_input_panels; + wl_signal_add(&ec->show_input_panel_signal, + &shell->show_input_panel_listener); + shell->hide_input_panel_listener.notify = hide_input_panels; + wl_signal_add(&ec->hide_input_panel_signal, + &shell->hide_input_panel_listener); + shell->update_input_panel_listener.notify = update_input_panels; + wl_signal_add(&ec->update_input_panel_signal, + &shell->update_input_panel_listener); + + wl_list_init(&shell->input_panel.surfaces); + + if (wl_global_create(shell->compositor->wl_display, + &wl_input_panel_interface, 1, + shell, bind_input_panel) == NULL) + return -1; + + return 0; +} diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c new file mode 100644 index 00000000..a8c4b3ea --- /dev/null +++ b/desktop-shell/shell.c @@ -0,0 +1,5641 @@ +/* + * Copyright © 2010-2012 Intel Corporation + * Copyright © 2011-2012 Collabora, Ltd. + * Copyright © 2013 Raspberry Pi Foundation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "shell.h" +#include "desktop-shell-server-protocol.h" +#include "workspaces-server-protocol.h" +#include "../shared/config-parser.h" +#include "xdg-shell-server-protocol.h" + +#define DEFAULT_NUM_WORKSPACES 1 +#define DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH 200 + +#ifndef static_assert +#define static_assert(cond, msg) +#endif + +struct focus_state { + struct weston_seat *seat; + struct workspace *ws; + struct weston_surface *keyboard_focus; + struct wl_list link; + struct wl_listener seat_destroy_listener; + struct wl_listener surface_destroy_listener; +}; + +struct shell_output { + struct desktop_shell *shell; + struct weston_output *output; + struct wl_listener destroy_listener; + struct wl_list link; +}; + +enum shell_surface_type { + SHELL_SURFACE_NONE, + SHELL_SURFACE_TOPLEVEL, + SHELL_SURFACE_POPUP, + SHELL_SURFACE_XWAYLAND +}; + +struct ping_timer { + struct wl_event_source *source; + uint32_t serial; +}; + +/* + * Surface stacking and ordering. + * + * This is handled using several linked lists of surfaces, organised into + * ‘layers’. The layers are ordered, and each of the surfaces in one layer are + * above all of the surfaces in the layer below. The set of layers is static and + * in the following order (top-most first): + * • Lock layer (only ever displayed on its own) + * • Cursor layer + * • Fullscreen layer + * • Panel layer + * • Input panel layer + * • Workspace layers + * • Background layer + * + * The list of layers may be manipulated to remove whole layers of surfaces from + * display. For example, when locking the screen, all layers except the lock + * layer are removed. + * + * A surface’s layer is modified on configuring the surface, in + * set_surface_type() (which is only called when the surface’s type change is + * _committed_). If a surface’s type changes (e.g. when making a window + * fullscreen) its layer changes too. + * + * In order to allow popup and transient surfaces to be correctly stacked above + * their parent surfaces, each surface tracks both its parent surface, and a + * linked list of its children. When a surface’s layer is updated, so are the + * layers of its children. Note that child surfaces are *not* the same as + * subsurfaces — child/parent surfaces are purely for maintaining stacking + * order. + * + * The children_link list of siblings of a surface (i.e. those surfaces which + * have the same parent) only contains weston_surfaces which have a + * shell_surface. Stacking is not implemented for non-shell_surface + * weston_surfaces. This means that the following implication does *not* hold: + * (shsurf->parent != NULL) ⇒ !wl_list_is_empty(shsurf->children_link) + */ + +struct shell_surface { + struct wl_resource *resource; + struct wl_signal destroy_signal; + + struct weston_surface *surface; + struct weston_view *view; + int32_t last_width, last_height; + struct wl_listener surface_destroy_listener; + struct weston_surface *parent; + struct wl_list children_list; /* child surfaces of this one */ + struct wl_list children_link; /* sibling surfaces of this one */ + struct desktop_shell *shell; + + enum shell_surface_type type; + char *title, *class; + int32_t saved_x, saved_y; + int32_t saved_width, saved_height; + bool saved_position_valid; + bool saved_size_valid; + bool saved_rotation_valid; + int unresponsive, grabbed; + + struct { + struct weston_transform transform; + struct weston_matrix rotation; + } rotation; + + struct { + struct wl_list grab_link; + int32_t x, y; + struct shell_seat *shseat; + uint32_t serial; + } popup; + + struct { + int32_t x, y; + uint32_t flags; + } transient; + + struct { + enum wl_shell_surface_fullscreen_method type; + struct weston_transform transform; /* matrix from x, y */ + uint32_t framerate; + struct weston_view *black_view; + } fullscreen; + + struct ping_timer *ping_timer; + + struct weston_transform workspace_transform; + + struct weston_output *fullscreen_output; + struct weston_output *output; + struct weston_output *recommended_output; + struct wl_list link; + + const struct weston_shell_client *client; + + struct { + bool maximized; + bool fullscreen; + bool relative; + } state, next_state; /* surface states */ + bool state_changed; +}; + +struct shell_grab { + struct weston_pointer_grab grab; + struct shell_surface *shsurf; + struct wl_listener shsurf_destroy_listener; +}; + +struct shell_touch_grab { + struct weston_touch_grab grab; + struct shell_surface *shsurf; + struct wl_listener shsurf_destroy_listener; + struct weston_touch *touch; +}; + +struct weston_move_grab { + struct shell_grab base; + wl_fixed_t dx, dy; +}; + +struct weston_touch_move_grab { + struct shell_touch_grab base; + wl_fixed_t dx, dy; +}; + +struct rotate_grab { + struct shell_grab base; + struct weston_matrix rotation; + struct { + float x; + float y; + } center; +}; + +struct shell_seat { + struct weston_seat *seat; + struct wl_listener seat_destroy_listener; + + struct { + struct weston_pointer_grab grab; + struct wl_list surfaces_list; + struct wl_client *client; + int32_t initial_up; + } popup_grab; +}; + +void +set_alpha_if_fullscreen(struct shell_surface *shsurf) +{ + if (shsurf && shsurf->state.fullscreen) + shsurf->fullscreen.black_view->alpha = 0.25; +} + +static struct desktop_shell * +shell_surface_get_shell(struct shell_surface *shsurf); + +static void +surface_rotate(struct shell_surface *surface, struct weston_seat *seat); + +static void +shell_fade_startup(struct desktop_shell *shell); + +static struct shell_seat * +get_shell_seat(struct weston_seat *seat); + +static void +shell_surface_update_child_surface_layers(struct shell_surface *shsurf); + +static bool +shell_surface_is_wl_shell_surface(struct shell_surface *shsurf); + +static bool +shell_surface_is_xdg_surface(struct shell_surface *shsurf); + +static bool +shell_surface_is_xdg_popup(struct shell_surface *shsurf); + +static void +shell_surface_set_parent(struct shell_surface *shsurf, + struct weston_surface *parent); + +static bool +shell_surface_is_top_fullscreen(struct shell_surface *shsurf) +{ + struct desktop_shell *shell; + struct weston_view *top_fs_ev; + + shell = shell_surface_get_shell(shsurf); + + if (wl_list_empty(&shell->fullscreen_layer.view_list)) + return false; + + top_fs_ev = container_of(shell->fullscreen_layer.view_list.next, + struct weston_view, + layer_link); + return (shsurf == get_shell_surface(top_fs_ev->surface)); +} + +static void +destroy_shell_grab_shsurf(struct wl_listener *listener, void *data) +{ + struct shell_grab *grab; + + grab = container_of(listener, struct shell_grab, + shsurf_destroy_listener); + + grab->shsurf = NULL; +} + +struct weston_view * +get_default_view(struct weston_surface *surface) +{ + struct shell_surface *shsurf; + struct weston_view *view; + + if (!surface || wl_list_empty(&surface->views)) + return NULL; + + shsurf = get_shell_surface(surface); + if (shsurf) + return shsurf->view; + + wl_list_for_each(view, &surface->views, surface_link) + if (weston_view_is_mapped(view)) + return view; + + return container_of(surface->views.next, struct weston_view, surface_link); +} + +static void +popup_grab_end(struct weston_pointer *pointer); + +static void +shell_grab_start(struct shell_grab *grab, + const struct weston_pointer_grab_interface *interface, + struct shell_surface *shsurf, + struct weston_pointer *pointer, + enum desktop_shell_cursor cursor) +{ + struct desktop_shell *shell = shsurf->shell; + + popup_grab_end(pointer); + + grab->grab.interface = interface; + grab->shsurf = shsurf; + grab->shsurf_destroy_listener.notify = destroy_shell_grab_shsurf; + wl_signal_add(&shsurf->destroy_signal, + &grab->shsurf_destroy_listener); + + shsurf->grabbed = 1; + weston_pointer_start_grab(pointer, &grab->grab); + if (shell->child.desktop_shell) { + desktop_shell_send_grab_cursor(shell->child.desktop_shell, + cursor); + weston_pointer_set_focus(pointer, + get_default_view(shell->grab_surface), + wl_fixed_from_int(0), + wl_fixed_from_int(0)); + } +} + +static void +shell_grab_end(struct shell_grab *grab) +{ + if (grab->shsurf) { + wl_list_remove(&grab->shsurf_destroy_listener.link); + grab->shsurf->grabbed = 0; + } + + weston_pointer_end_grab(grab->grab.pointer); +} + +static void +shell_touch_grab_start(struct shell_touch_grab *grab, + const struct weston_touch_grab_interface *interface, + struct shell_surface *shsurf, + struct weston_touch *touch) +{ + struct desktop_shell *shell = shsurf->shell; + + grab->grab.interface = interface; + grab->shsurf = shsurf; + grab->shsurf_destroy_listener.notify = destroy_shell_grab_shsurf; + wl_signal_add(&shsurf->destroy_signal, + &grab->shsurf_destroy_listener); + + grab->touch = touch; + shsurf->grabbed = 1; + + weston_touch_start_grab(touch, &grab->grab); + if (shell->child.desktop_shell) + weston_touch_set_focus(touch->seat, + get_default_view(shell->grab_surface)); +} + +static void +shell_touch_grab_end(struct shell_touch_grab *grab) +{ + if (grab->shsurf) { + wl_list_remove(&grab->shsurf_destroy_listener.link); + grab->shsurf->grabbed = 0; + } + + weston_touch_end_grab(grab->touch); +} + +static void +center_on_output(struct weston_view *view, + struct weston_output *output); + +static enum weston_keyboard_modifier +get_modifier(char *modifier) +{ + if (!modifier) + return MODIFIER_SUPER; + + if (!strcmp("ctrl", modifier)) + return MODIFIER_CTRL; + else if (!strcmp("alt", modifier)) + return MODIFIER_ALT; + else if (!strcmp("super", modifier)) + return MODIFIER_SUPER; + else + return MODIFIER_SUPER; +} + +static enum animation_type +get_animation_type(char *animation) +{ + if (!strcmp("zoom", animation)) + return ANIMATION_ZOOM; + else if (!strcmp("fade", animation)) + return ANIMATION_FADE; + else if (!strcmp("dim-layer", animation)) + return ANIMATION_DIM_LAYER; + else + return ANIMATION_NONE; +} + +static void +shell_configuration(struct desktop_shell *shell) +{ + struct weston_config_section *section; + int duration; + char *s; + + section = weston_config_get_section(shell->compositor->config, + "screensaver", NULL, NULL); + weston_config_section_get_string(section, + "path", &shell->screensaver.path, NULL); + weston_config_section_get_int(section, "duration", &duration, 60); + shell->screensaver.duration = duration * 1000; + + section = weston_config_get_section(shell->compositor->config, + "shell", NULL, NULL); + weston_config_section_get_string(section, + "client", &s, LIBEXECDIR "/" WESTON_SHELL_CLIENT); + shell->client = s; + weston_config_section_get_string(section, + "binding-modifier", &s, "super"); + shell->binding_modifier = get_modifier(s); + free(s); + weston_config_section_get_string(section, "animation", &s, "none"); + shell->win_animation_type = get_animation_type(s); + free(s); + weston_config_section_get_string(section, + "startup-animation", &s, "fade"); + shell->startup_animation_type = get_animation_type(s); + free(s); + if (shell->startup_animation_type == ANIMATION_ZOOM) + shell->startup_animation_type = ANIMATION_NONE; + weston_config_section_get_string(section, "focus-animation", &s, "none"); + shell->focus_animation_type = get_animation_type(s); + free(s); + weston_config_section_get_uint(section, "num-workspaces", + &shell->workspaces.num, + DEFAULT_NUM_WORKSPACES); +} + +struct weston_output * +get_default_output(struct weston_compositor *compositor) +{ + return container_of(compositor->output_list.next, + struct weston_output, link); +} + + +/* no-op func for checking focus surface */ +static void +focus_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy) +{ +} + +static struct focus_surface * +get_focus_surface(struct weston_surface *surface) +{ + if (surface->configure == focus_surface_configure) + return surface->configure_private; + else + return NULL; +} + +static bool +is_focus_surface (struct weston_surface *es) +{ + return (es->configure == focus_surface_configure); +} + +static bool +is_focus_view (struct weston_view *view) +{ + return is_focus_surface (view->surface); +} + +static struct focus_surface * +create_focus_surface(struct weston_compositor *ec, + struct weston_output *output) +{ + struct focus_surface *fsurf = NULL; + struct weston_surface *surface = NULL; + + fsurf = malloc(sizeof *fsurf); + if (!fsurf) + return NULL; + + fsurf->surface = weston_surface_create(ec); + surface = fsurf->surface; + if (surface == NULL) { + free(fsurf); + return NULL; + } + + surface->configure = focus_surface_configure; + surface->output = output; + surface->configure_private = fsurf; + + fsurf->view = weston_view_create (surface); + fsurf->view->output = output; + + weston_surface_set_size(surface, output->width, output->height); + weston_view_set_position(fsurf->view, output->x, output->y); + weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0); + pixman_region32_fini(&surface->opaque); + pixman_region32_init_rect(&surface->opaque, output->x, output->y, + output->width, output->height); + pixman_region32_fini(&surface->input); + pixman_region32_init(&surface->input); + + wl_list_init(&fsurf->workspace_transform.link); + + return fsurf; +} + +static void +focus_surface_destroy(struct focus_surface *fsurf) +{ + weston_surface_destroy(fsurf->surface); + free(fsurf); +} + +static void +focus_animation_done(struct weston_view_animation *animation, void *data) +{ + struct workspace *ws = data; + + ws->focus_animation = NULL; +} + +static void +focus_state_destroy(struct focus_state *state) +{ + wl_list_remove(&state->seat_destroy_listener.link); + wl_list_remove(&state->surface_destroy_listener.link); + free(state); +} + +static void +focus_state_seat_destroy(struct wl_listener *listener, void *data) +{ + struct focus_state *state = container_of(listener, + struct focus_state, + seat_destroy_listener); + + wl_list_remove(&state->link); + focus_state_destroy(state); +} + +static void +focus_state_surface_destroy(struct wl_listener *listener, void *data) +{ + struct focus_state *state = container_of(listener, + struct focus_state, + surface_destroy_listener); + struct desktop_shell *shell; + struct weston_surface *main_surface, *next; + struct weston_view *view; + + main_surface = weston_surface_get_main_surface(state->keyboard_focus); + + next = NULL; + wl_list_for_each(view, &state->ws->layer.view_list, layer_link) { + if (view->surface == main_surface) + continue; + if (is_focus_view(view)) + continue; + + next = view->surface; + break; + } + + /* if the focus was a sub-surface, activate its main surface */ + if (main_surface != state->keyboard_focus) + next = main_surface; + + shell = state->seat->compositor->shell_interface.shell; + if (next) { + state->keyboard_focus = NULL; + activate(shell, next, state->seat); + } else { + if (shell->focus_animation_type == ANIMATION_DIM_LAYER) { + if (state->ws->focus_animation) + weston_view_animation_destroy(state->ws->focus_animation); + + state->ws->focus_animation = weston_fade_run( + state->ws->fsurf_front->view, + state->ws->fsurf_front->view->alpha, 0.0, 300, + focus_animation_done, state->ws); + } + + wl_list_remove(&state->link); + focus_state_destroy(state); + } +} + +static struct focus_state * +focus_state_create(struct weston_seat *seat, struct workspace *ws) +{ + struct focus_state *state; + + state = malloc(sizeof *state); + if (state == NULL) + return NULL; + + state->keyboard_focus = NULL; + state->ws = ws; + state->seat = seat; + wl_list_insert(&ws->focus_list, &state->link); + + state->seat_destroy_listener.notify = focus_state_seat_destroy; + state->surface_destroy_listener.notify = focus_state_surface_destroy; + wl_signal_add(&seat->destroy_signal, + &state->seat_destroy_listener); + wl_list_init(&state->surface_destroy_listener.link); + + return state; +} + +static struct focus_state * +ensure_focus_state(struct desktop_shell *shell, struct weston_seat *seat) +{ + struct workspace *ws = get_current_workspace(shell); + struct focus_state *state; + + wl_list_for_each(state, &ws->focus_list, link) + if (state->seat == seat) + break; + + if (&state->link == &ws->focus_list) + state = focus_state_create(seat, ws); + + return state; +} + +static void +restore_focus_state(struct desktop_shell *shell, struct workspace *ws) +{ + struct focus_state *state, *next; + struct weston_surface *surface; + + wl_list_for_each_safe(state, next, &ws->focus_list, link) { + surface = state->keyboard_focus; + + weston_keyboard_set_focus(state->seat->keyboard, surface); + } +} + +static void +replace_focus_state(struct desktop_shell *shell, struct workspace *ws, + struct weston_seat *seat) +{ + struct focus_state *state; + struct weston_surface *surface; + + wl_list_for_each(state, &ws->focus_list, link) { + if (state->seat == seat) { + surface = seat->keyboard->focus; + state->keyboard_focus = surface; + return; + } + } +} + +static void +drop_focus_state(struct desktop_shell *shell, struct workspace *ws, + struct weston_surface *surface) +{ + struct focus_state *state; + + wl_list_for_each(state, &ws->focus_list, link) + if (state->keyboard_focus == surface) + state->keyboard_focus = NULL; +} + +static void +animate_focus_change(struct desktop_shell *shell, struct workspace *ws, + struct weston_view *from, struct weston_view *to) +{ + struct weston_output *output; + bool focus_surface_created = false; + + /* FIXME: Only support dim animation using two layers */ + if (from == to || shell->focus_animation_type != ANIMATION_DIM_LAYER) + return; + + output = get_default_output(shell->compositor); + if (ws->fsurf_front == NULL && (from || to)) { + ws->fsurf_front = create_focus_surface(shell->compositor, output); + ws->fsurf_back = create_focus_surface(shell->compositor, output); + ws->fsurf_front->view->alpha = 0.0; + ws->fsurf_back->view->alpha = 0.0; + focus_surface_created = true; + } else { + wl_list_remove(&ws->fsurf_front->view->layer_link); + wl_list_remove(&ws->fsurf_back->view->layer_link); + } + + if (ws->focus_animation) { + weston_view_animation_destroy(ws->focus_animation); + ws->focus_animation = NULL; + } + + if (to) + wl_list_insert(&to->layer_link, + &ws->fsurf_front->view->layer_link); + else if (from) + wl_list_insert(&ws->layer.view_list, + &ws->fsurf_front->view->layer_link); + + if (focus_surface_created) { + ws->focus_animation = weston_fade_run( + ws->fsurf_front->view, + ws->fsurf_front->view->alpha, 0.6, 300, + focus_animation_done, ws); + } else if (from) { + wl_list_insert(&from->layer_link, + &ws->fsurf_back->view->layer_link); + ws->focus_animation = weston_stable_fade_run( + ws->fsurf_front->view, 0.0, + ws->fsurf_back->view, 0.6, + focus_animation_done, ws); + } else if (to) { + wl_list_insert(&ws->layer.view_list, + &ws->fsurf_back->view->layer_link); + ws->focus_animation = weston_stable_fade_run( + ws->fsurf_front->view, 0.0, + ws->fsurf_back->view, 0.6, + focus_animation_done, ws); + } +} + +static void +workspace_destroy(struct workspace *ws) +{ + struct focus_state *state, *next; + + wl_list_for_each_safe(state, next, &ws->focus_list, link) + focus_state_destroy(state); + + if (ws->fsurf_front) + focus_surface_destroy(ws->fsurf_front); + if (ws->fsurf_back) + focus_surface_destroy(ws->fsurf_back); + + free(ws); +} + +static void +seat_destroyed(struct wl_listener *listener, void *data) +{ + struct weston_seat *seat = data; + struct focus_state *state, *next; + struct workspace *ws = container_of(listener, + struct workspace, + seat_destroyed_listener); + + wl_list_for_each_safe(state, next, &ws->focus_list, link) + if (state->seat == seat) + wl_list_remove(&state->link); +} + +static struct workspace * +workspace_create(void) +{ + struct workspace *ws = malloc(sizeof *ws); + if (ws == NULL) + return NULL; + + weston_layer_init(&ws->layer, NULL); + + wl_list_init(&ws->focus_list); + wl_list_init(&ws->seat_destroyed_listener.link); + ws->seat_destroyed_listener.notify = seat_destroyed; + ws->fsurf_front = NULL; + ws->fsurf_back = NULL; + ws->focus_animation = NULL; + + return ws; +} + +static int +workspace_is_empty(struct workspace *ws) +{ + return wl_list_empty(&ws->layer.view_list); +} + +static struct workspace * +get_workspace(struct desktop_shell *shell, unsigned int index) +{ + struct workspace **pws = shell->workspaces.array.data; + assert(index < shell->workspaces.num); + pws += index; + return *pws; +} + +struct workspace * +get_current_workspace(struct desktop_shell *shell) +{ + return get_workspace(shell, shell->workspaces.current); +} + +static void +activate_workspace(struct desktop_shell *shell, unsigned int index) +{ + struct workspace *ws; + + ws = get_workspace(shell, index); + wl_list_insert(&shell->panel_layer.link, &ws->layer.link); + + shell->workspaces.current = index; +} + +static unsigned int +get_output_height(struct weston_output *output) +{ + return abs(output->region.extents.y1 - output->region.extents.y2); +} + +static void +view_translate(struct workspace *ws, struct weston_view *view, double d) +{ + struct weston_transform *transform; + + if (is_focus_view(view)) { + struct focus_surface *fsurf = get_focus_surface(view->surface); + transform = &fsurf->workspace_transform; + } else { + struct shell_surface *shsurf = get_shell_surface(view->surface); + transform = &shsurf->workspace_transform; + } + + if (wl_list_empty(&transform->link)) + wl_list_insert(view->geometry.transformation_list.prev, + &transform->link); + + weston_matrix_init(&transform->matrix); + weston_matrix_translate(&transform->matrix, + 0.0, d, 0.0); + weston_view_geometry_dirty(view); +} + +static void +workspace_translate_out(struct workspace *ws, double fraction) +{ + struct weston_view *view; + unsigned int height; + double d; + + wl_list_for_each(view, &ws->layer.view_list, layer_link) { + height = get_output_height(view->surface->output); + d = height * fraction; + + view_translate(ws, view, d); + } +} + +static void +workspace_translate_in(struct workspace *ws, double fraction) +{ + struct weston_view *view; + unsigned int height; + double d; + + wl_list_for_each(view, &ws->layer.view_list, layer_link) { + height = get_output_height(view->surface->output); + + if (fraction > 0) + d = -(height - height * fraction); + else + d = height + height * fraction; + + view_translate(ws, view, d); + } +} + +static void +broadcast_current_workspace_state(struct desktop_shell *shell) +{ + struct wl_resource *resource; + + wl_resource_for_each(resource, &shell->workspaces.client_list) + workspace_manager_send_state(resource, + shell->workspaces.current, + shell->workspaces.num); +} + +static void +reverse_workspace_change_animation(struct desktop_shell *shell, + unsigned int index, + struct workspace *from, + struct workspace *to) +{ + shell->workspaces.current = index; + + shell->workspaces.anim_to = to; + shell->workspaces.anim_from = from; + shell->workspaces.anim_dir = -1 * shell->workspaces.anim_dir; + shell->workspaces.anim_timestamp = 0; + + weston_compositor_schedule_repaint(shell->compositor); +} + +static void +workspace_deactivate_transforms(struct workspace *ws) +{ + struct weston_view *view; + struct weston_transform *transform; + + wl_list_for_each(view, &ws->layer.view_list, layer_link) { + if (is_focus_view(view)) { + struct focus_surface *fsurf = get_focus_surface(view->surface); + transform = &fsurf->workspace_transform; + } else { + struct shell_surface *shsurf = get_shell_surface(view->surface); + transform = &shsurf->workspace_transform; + } + + if (!wl_list_empty(&transform->link)) { + wl_list_remove(&transform->link); + wl_list_init(&transform->link); + } + weston_view_geometry_dirty(view); + } +} + +static void +finish_workspace_change_animation(struct desktop_shell *shell, + struct workspace *from, + struct workspace *to) +{ + weston_compositor_schedule_repaint(shell->compositor); + + wl_list_remove(&shell->workspaces.animation.link); + workspace_deactivate_transforms(from); + workspace_deactivate_transforms(to); + shell->workspaces.anim_to = NULL; + + wl_list_remove(&shell->workspaces.anim_from->layer.link); +} + +static void +animate_workspace_change_frame(struct weston_animation *animation, + struct weston_output *output, uint32_t msecs) +{ + struct desktop_shell *shell = + container_of(animation, struct desktop_shell, + workspaces.animation); + struct workspace *from = shell->workspaces.anim_from; + struct workspace *to = shell->workspaces.anim_to; + uint32_t t; + double x, y; + + if (workspace_is_empty(from) && workspace_is_empty(to)) { + finish_workspace_change_animation(shell, from, to); + return; + } + + if (shell->workspaces.anim_timestamp == 0) { + if (shell->workspaces.anim_current == 0.0) + shell->workspaces.anim_timestamp = msecs; + else + shell->workspaces.anim_timestamp = + msecs - + /* Invers of movement function 'y' below. */ + (asin(1.0 - shell->workspaces.anim_current) * + DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH * + M_2_PI); + } + + t = msecs - shell->workspaces.anim_timestamp; + + /* + * x = [0, π/2] + * y(x) = sin(x) + */ + x = t * (1.0/DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) * M_PI_2; + y = sin(x); + + if (t < DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) { + weston_compositor_schedule_repaint(shell->compositor); + + workspace_translate_out(from, shell->workspaces.anim_dir * y); + workspace_translate_in(to, shell->workspaces.anim_dir * y); + shell->workspaces.anim_current = y; + + weston_compositor_schedule_repaint(shell->compositor); + } + else + finish_workspace_change_animation(shell, from, to); +} + +static void +animate_workspace_change(struct desktop_shell *shell, + unsigned int index, + struct workspace *from, + struct workspace *to) +{ + struct weston_output *output; + + int dir; + + if (index > shell->workspaces.current) + dir = -1; + else + dir = 1; + + shell->workspaces.current = index; + + shell->workspaces.anim_dir = dir; + shell->workspaces.anim_from = from; + shell->workspaces.anim_to = to; + shell->workspaces.anim_current = 0.0; + shell->workspaces.anim_timestamp = 0; + + output = container_of(shell->compositor->output_list.next, + struct weston_output, link); + wl_list_insert(&output->animation_list, + &shell->workspaces.animation.link); + + wl_list_insert(from->layer.link.prev, &to->layer.link); + + workspace_translate_in(to, 0); + + restore_focus_state(shell, to); + + weston_compositor_schedule_repaint(shell->compositor); +} + +static void +update_workspace(struct desktop_shell *shell, unsigned int index, + struct workspace *from, struct workspace *to) +{ + shell->workspaces.current = index; + wl_list_insert(&from->layer.link, &to->layer.link); + wl_list_remove(&from->layer.link); +} + +static void +change_workspace(struct desktop_shell *shell, unsigned int index) +{ + struct workspace *from; + struct workspace *to; + struct focus_state *state; + + if (index == shell->workspaces.current) + return; + + /* Don't change workspace when there is any fullscreen surfaces. */ + if (!wl_list_empty(&shell->fullscreen_layer.view_list)) + return; + + from = get_current_workspace(shell); + to = get_workspace(shell, index); + + if (shell->workspaces.anim_from == to && + shell->workspaces.anim_to == from) { + restore_focus_state(shell, to); + reverse_workspace_change_animation(shell, index, from, to); + broadcast_current_workspace_state(shell); + return; + } + + if (shell->workspaces.anim_to != NULL) + finish_workspace_change_animation(shell, + shell->workspaces.anim_from, + shell->workspaces.anim_to); + + restore_focus_state(shell, to); + + if (shell->focus_animation_type != ANIMATION_NONE) { + wl_list_for_each(state, &from->focus_list, link) + if (state->keyboard_focus) + animate_focus_change(shell, from, + get_default_view(state->keyboard_focus), NULL); + + wl_list_for_each(state, &to->focus_list, link) + if (state->keyboard_focus) + animate_focus_change(shell, to, + NULL, get_default_view(state->keyboard_focus)); + } + + if (workspace_is_empty(to) && workspace_is_empty(from)) + update_workspace(shell, index, from, to); + else + animate_workspace_change(shell, index, from, to); + + broadcast_current_workspace_state(shell); +} + +static bool +workspace_has_only(struct workspace *ws, struct weston_surface *surface) +{ + struct wl_list *list = &ws->layer.view_list; + struct wl_list *e; + + if (wl_list_empty(list)) + return false; + + e = list->next; + + if (e->next != list) + return false; + + return container_of(e, struct weston_view, layer_link)->surface == surface; +} + +static void +move_surface_to_workspace(struct desktop_shell *shell, + struct shell_surface *shsurf, + uint32_t workspace) +{ + struct workspace *from; + struct workspace *to; + struct weston_seat *seat; + struct weston_surface *focus; + struct weston_view *view; + + if (workspace == shell->workspaces.current) + return; + + view = get_default_view(shsurf->surface); + if (!view) + return; + + assert(weston_surface_get_main_surface(view->surface) == view->surface); + + if (workspace >= shell->workspaces.num) + workspace = shell->workspaces.num - 1; + + from = get_current_workspace(shell); + to = get_workspace(shell, workspace); + + wl_list_remove(&view->layer_link); + wl_list_insert(&to->layer.view_list, &view->layer_link); + + shell_surface_update_child_surface_layers(shsurf); + + drop_focus_state(shell, from, view->surface); + wl_list_for_each(seat, &shell->compositor->seat_list, link) { + if (!seat->keyboard) + continue; + + focus = weston_surface_get_main_surface(seat->keyboard->focus); + if (focus == view->surface) + weston_keyboard_set_focus(seat->keyboard, NULL); + } + + weston_view_damage_below(view); +} + +static void +take_surface_to_workspace_by_seat(struct desktop_shell *shell, + struct weston_seat *seat, + unsigned int index) +{ + struct weston_surface *surface; + struct weston_view *view; + struct shell_surface *shsurf; + struct workspace *from; + struct workspace *to; + struct focus_state *state; + + surface = weston_surface_get_main_surface(seat->keyboard->focus); + view = get_default_view(surface); + if (view == NULL || + index == shell->workspaces.current || + is_focus_view(view)) + return; + + from = get_current_workspace(shell); + to = get_workspace(shell, index); + + wl_list_remove(&view->layer_link); + wl_list_insert(&to->layer.view_list, &view->layer_link); + + shsurf = get_shell_surface(surface); + if (shsurf != NULL) + shell_surface_update_child_surface_layers(shsurf); + + replace_focus_state(shell, to, seat); + drop_focus_state(shell, from, surface); + + if (shell->workspaces.anim_from == to && + shell->workspaces.anim_to == from) { + wl_list_remove(&to->layer.link); + wl_list_insert(from->layer.link.prev, &to->layer.link); + + reverse_workspace_change_animation(shell, index, from, to); + broadcast_current_workspace_state(shell); + + return; + } + + if (shell->workspaces.anim_to != NULL) + finish_workspace_change_animation(shell, + shell->workspaces.anim_from, + shell->workspaces.anim_to); + + if (workspace_is_empty(from) && + workspace_has_only(to, surface)) + update_workspace(shell, index, from, to); + else { + if (shsurf != NULL && + wl_list_empty(&shsurf->workspace_transform.link)) + wl_list_insert(&shell->workspaces.anim_sticky_list, + &shsurf->workspace_transform.link); + + animate_workspace_change(shell, index, from, to); + } + + broadcast_current_workspace_state(shell); + + state = ensure_focus_state(shell, seat); + if (state != NULL) + state->keyboard_focus = surface; +} + +static void +workspace_manager_move_surface(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *surface_resource, + uint32_t workspace) +{ + struct desktop_shell *shell = wl_resource_get_user_data(resource); + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + struct weston_surface *main_surface; + struct shell_surface *shell_surface; + + main_surface = weston_surface_get_main_surface(surface); + shell_surface = get_shell_surface(main_surface); + if (shell_surface == NULL) + return; + + move_surface_to_workspace(shell, shell_surface, workspace); +} + +static const struct workspace_manager_interface workspace_manager_implementation = { + workspace_manager_move_surface, +}; + +static void +unbind_resource(struct wl_resource *resource) +{ + wl_list_remove(wl_resource_get_link(resource)); +} + +static void +bind_workspace_manager(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct desktop_shell *shell = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, + &workspace_manager_interface, 1, id); + + if (resource == NULL) { + weston_log("couldn't add workspace manager object"); + return; + } + + wl_resource_set_implementation(resource, + &workspace_manager_implementation, + shell, unbind_resource); + wl_list_insert(&shell->workspaces.client_list, + wl_resource_get_link(resource)); + + workspace_manager_send_state(resource, + shell->workspaces.current, + shell->workspaces.num); +} + +static void +touch_move_grab_down(struct weston_touch_grab *grab, uint32_t time, + int touch_id, wl_fixed_t sx, wl_fixed_t sy) +{ +} + +static void +touch_move_grab_up(struct weston_touch_grab *grab, uint32_t time, int touch_id) +{ + struct weston_touch_move_grab *move = + (struct weston_touch_move_grab *) container_of( + grab, struct shell_touch_grab, grab); + + if (grab->touch->num_tp == 0) { + shell_touch_grab_end(&move->base); + free(move); + } +} + +static void +touch_move_grab_motion(struct weston_touch_grab *grab, uint32_t time, + int touch_id, wl_fixed_t sx, wl_fixed_t sy) +{ + struct weston_touch_move_grab *move = (struct weston_touch_move_grab *) grab; + struct shell_surface *shsurf = move->base.shsurf; + struct weston_surface *es; + int dx = wl_fixed_to_int(grab->touch->grab_x + move->dx); + int dy = wl_fixed_to_int(grab->touch->grab_y + move->dy); + + if (!shsurf) + return; + + es = shsurf->surface; + + weston_view_set_position(shsurf->view, dx, dy); + + weston_compositor_schedule_repaint(es->compositor); +} + +static void +touch_move_grab_cancel(struct weston_touch_grab *grab) +{ + struct weston_touch_move_grab *move = + (struct weston_touch_move_grab *) container_of( + grab, struct shell_touch_grab, grab); + + shell_touch_grab_end(&move->base); + free(move); +} + +static const struct weston_touch_grab_interface touch_move_grab_interface = { + touch_move_grab_down, + touch_move_grab_up, + touch_move_grab_motion, + touch_move_grab_cancel, +}; + +static int +surface_touch_move(struct shell_surface *shsurf, struct weston_seat *seat) +{ + struct weston_touch_move_grab *move; + + if (!shsurf) + return -1; + + if (shsurf->state.fullscreen) + return 0; + if (shsurf->grabbed) + return 0; + + move = malloc(sizeof *move); + if (!move) + return -1; + + move->dx = wl_fixed_from_double(shsurf->view->geometry.x) - + seat->touch->grab_x; + move->dy = wl_fixed_from_double(shsurf->view->geometry.y) - + seat->touch->grab_y; + + shell_touch_grab_start(&move->base, &touch_move_grab_interface, shsurf, + seat->touch); + + return 0; +} + +static void +noop_grab_focus(struct weston_pointer_grab *grab) +{ +} + +static void +move_grab_motion(struct weston_pointer_grab *grab, uint32_t time, + wl_fixed_t x, wl_fixed_t y) +{ + struct weston_move_grab *move = (struct weston_move_grab *) grab; + struct weston_pointer *pointer = grab->pointer; + struct shell_surface *shsurf = move->base.shsurf; + int dx, dy; + + weston_pointer_move(pointer, x, y); + dx = wl_fixed_to_int(pointer->x + move->dx); + dy = wl_fixed_to_int(pointer->y + move->dy); + + if (!shsurf) + return; + + weston_view_set_position(shsurf->view, dx, dy); + + weston_compositor_schedule_repaint(shsurf->surface->compositor); +} + +static void +move_grab_button(struct weston_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state_w) +{ + struct shell_grab *shell_grab = container_of(grab, struct shell_grab, + grab); + struct weston_pointer *pointer = grab->pointer; + enum wl_pointer_button_state state = state_w; + + if (pointer->button_count == 0 && + state == WL_POINTER_BUTTON_STATE_RELEASED) { + shell_grab_end(shell_grab); + free(grab); + } +} + +static void +move_grab_cancel(struct weston_pointer_grab *grab) +{ + struct shell_grab *shell_grab = + container_of(grab, struct shell_grab, grab); + + shell_grab_end(shell_grab); + free(grab); +} + +static const struct weston_pointer_grab_interface move_grab_interface = { + noop_grab_focus, + move_grab_motion, + move_grab_button, + move_grab_cancel, +}; + +static int +surface_move(struct shell_surface *shsurf, struct weston_seat *seat) +{ + struct weston_move_grab *move; + + if (!shsurf) + return -1; + + if (shsurf->grabbed) + return 0; + if (shsurf->state.fullscreen) + return 0; + + move = malloc(sizeof *move); + if (!move) + return -1; + + move->dx = wl_fixed_from_double(shsurf->view->geometry.x) - + seat->pointer->grab_x; + move->dy = wl_fixed_from_double(shsurf->view->geometry.y) - + seat->pointer->grab_y; + + shell_grab_start(&move->base, &move_grab_interface, shsurf, + seat->pointer, DESKTOP_SHELL_CURSOR_MOVE); + + return 0; +} + +static void +common_surface_move(struct wl_resource *resource, + struct wl_resource *seat_resource, uint32_t serial) +{ + struct weston_seat *seat = wl_resource_get_user_data(seat_resource); + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + struct weston_surface *surface; + + if (seat->pointer && + seat->pointer->focus && + seat->pointer->button_count > 0 && + seat->pointer->grab_serial == serial) { + surface = weston_surface_get_main_surface(seat->pointer->focus->surface); + if ((surface == shsurf->surface) && + (surface_move(shsurf, seat) < 0)) + wl_resource_post_no_memory(resource); + } else if (seat->touch && + seat->touch->focus && + seat->touch->grab_serial == serial) { + surface = weston_surface_get_main_surface(seat->touch->focus->surface); + if ((surface == shsurf->surface) && + (surface_touch_move(shsurf, seat) < 0)) + wl_resource_post_no_memory(resource); + } +} + +static void +shell_surface_move(struct wl_client *client, struct wl_resource *resource, + struct wl_resource *seat_resource, uint32_t serial) +{ + common_surface_move(resource, seat_resource, serial); +} + +struct weston_resize_grab { + struct shell_grab base; + uint32_t edges; + int32_t width, height; +}; + +static void +resize_grab_motion(struct weston_pointer_grab *grab, uint32_t time, + wl_fixed_t x, wl_fixed_t y) +{ + struct weston_resize_grab *resize = (struct weston_resize_grab *) grab; + struct weston_pointer *pointer = grab->pointer; + struct shell_surface *shsurf = resize->base.shsurf; + int32_t width, height; + wl_fixed_t from_x, from_y; + wl_fixed_t to_x, to_y; + + weston_pointer_move(pointer, x, y); + + if (!shsurf) + return; + + weston_view_from_global_fixed(shsurf->view, + pointer->grab_x, pointer->grab_y, + &from_x, &from_y); + weston_view_from_global_fixed(shsurf->view, + pointer->x, pointer->y, &to_x, &to_y); + + width = resize->width; + if (resize->edges & WL_SHELL_SURFACE_RESIZE_LEFT) { + width += wl_fixed_to_int(from_x - to_x); + } else if (resize->edges & WL_SHELL_SURFACE_RESIZE_RIGHT) { + width += wl_fixed_to_int(to_x - from_x); + } + + height = resize->height; + if (resize->edges & WL_SHELL_SURFACE_RESIZE_TOP) { + height += wl_fixed_to_int(from_y - to_y); + } else if (resize->edges & WL_SHELL_SURFACE_RESIZE_BOTTOM) { + height += wl_fixed_to_int(to_y - from_y); + } + + shsurf->client->send_configure(shsurf->surface, + resize->edges, width, height); +} + +static void +send_configure(struct weston_surface *surface, + uint32_t edges, int32_t width, int32_t height) +{ + struct shell_surface *shsurf = get_shell_surface(surface); + + wl_shell_surface_send_configure(shsurf->resource, + edges, width, height); +} + +static const struct weston_shell_client shell_client = { + send_configure +}; + +static void +resize_grab_button(struct weston_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state_w) +{ + struct weston_resize_grab *resize = (struct weston_resize_grab *) grab; + struct weston_pointer *pointer = grab->pointer; + enum wl_pointer_button_state state = state_w; + + if (pointer->button_count == 0 && + state == WL_POINTER_BUTTON_STATE_RELEASED) { + shell_grab_end(&resize->base); + free(grab); + } +} + +static void +resize_grab_cancel(struct weston_pointer_grab *grab) +{ + struct weston_resize_grab *resize = (struct weston_resize_grab *) grab; + + shell_grab_end(&resize->base); + free(grab); +} + +static const struct weston_pointer_grab_interface resize_grab_interface = { + noop_grab_focus, + resize_grab_motion, + resize_grab_button, + resize_grab_cancel, +}; + +/* + * Returns the bounding box of a surface and all its sub-surfaces, + * in the surface coordinates system. */ +static void +surface_subsurfaces_boundingbox(struct weston_surface *surface, int32_t *x, + int32_t *y, int32_t *w, int32_t *h) { + pixman_region32_t region; + pixman_box32_t *box; + struct weston_subsurface *subsurface; + + pixman_region32_init_rect(®ion, 0, 0, + surface->width, + surface->height); + + wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) { + pixman_region32_union_rect(®ion, ®ion, + subsurface->position.x, + subsurface->position.y, + subsurface->surface->width, + subsurface->surface->height); + } + + box = pixman_region32_extents(®ion); + if (x) + *x = box->x1; + if (y) + *y = box->y1; + if (w) + *w = box->x2 - box->x1; + if (h) + *h = box->y2 - box->y1; + + pixman_region32_fini(®ion); +} + +static int +surface_resize(struct shell_surface *shsurf, + struct weston_seat *seat, uint32_t edges) +{ + struct weston_resize_grab *resize; + + if (shsurf->state.fullscreen || shsurf->state.maximized) + return 0; + + if (edges == 0 || edges > 15 || + (edges & 3) == 3 || (edges & 12) == 12) + return 0; + + resize = malloc(sizeof *resize); + if (!resize) + return -1; + + resize->edges = edges; + surface_subsurfaces_boundingbox(shsurf->surface, NULL, NULL, + &resize->width, &resize->height); + + shell_grab_start(&resize->base, &resize_grab_interface, shsurf, + seat->pointer, edges); + + return 0; +} + +static void +common_surface_resize(struct wl_resource *resource, + struct wl_resource *seat_resource, uint32_t serial, + uint32_t edges) +{ + struct weston_seat *seat = wl_resource_get_user_data(seat_resource); + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + struct weston_surface *surface; + + if (shsurf->state.fullscreen) + return; + + if (seat->pointer->button_count == 0 || + seat->pointer->grab_serial != serial || + seat->pointer->focus == NULL) + return; + + surface = weston_surface_get_main_surface(seat->pointer->focus->surface); + if (surface != shsurf->surface) + return; + + if (surface_resize(shsurf, seat, edges) < 0) + wl_resource_post_no_memory(resource); +} + +static void +shell_surface_resize(struct wl_client *client, struct wl_resource *resource, + struct wl_resource *seat_resource, uint32_t serial, + uint32_t edges) +{ + common_surface_resize(resource, seat_resource, serial, edges); +} + +static void +end_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer); + +static void +busy_cursor_grab_focus(struct weston_pointer_grab *base) +{ + struct shell_grab *grab = (struct shell_grab *) base; + struct weston_pointer *pointer = base->pointer; + struct weston_view *view; + wl_fixed_t sx, sy; + + view = weston_compositor_pick_view(pointer->seat->compositor, + pointer->x, pointer->y, + &sx, &sy); + + if (!grab->shsurf || grab->shsurf->surface != view->surface) + end_busy_cursor(grab->shsurf, pointer); +} + +static void +busy_cursor_grab_motion(struct weston_pointer_grab *grab, uint32_t time, + wl_fixed_t x, wl_fixed_t y) +{ + weston_pointer_move(grab->pointer, x, y); +} + +static void +busy_cursor_grab_button(struct weston_pointer_grab *base, + uint32_t time, uint32_t button, uint32_t state) +{ + struct shell_grab *grab = (struct shell_grab *) base; + struct shell_surface *shsurf = grab->shsurf; + struct weston_seat *seat = grab->grab.pointer->seat; + + if (shsurf && button == BTN_LEFT && state) { + activate(shsurf->shell, shsurf->surface, seat); + surface_move(shsurf, seat); + } else if (shsurf && button == BTN_RIGHT && state) { + activate(shsurf->shell, shsurf->surface, seat); + surface_rotate(shsurf, seat); + } +} + +static void +busy_cursor_grab_cancel(struct weston_pointer_grab *base) +{ + struct shell_grab *grab = (struct shell_grab *) base; + + shell_grab_end(grab); + free(grab); +} + +static const struct weston_pointer_grab_interface busy_cursor_grab_interface = { + busy_cursor_grab_focus, + busy_cursor_grab_motion, + busy_cursor_grab_button, + busy_cursor_grab_cancel, +}; + +static void +set_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer) +{ + struct shell_grab *grab; + + grab = malloc(sizeof *grab); + if (!grab) + return; + + shell_grab_start(grab, &busy_cursor_grab_interface, shsurf, pointer, + DESKTOP_SHELL_CURSOR_BUSY); +} + +static void +end_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer) +{ + struct shell_grab *grab = (struct shell_grab *) pointer->grab; + + if (grab->grab.interface == &busy_cursor_grab_interface && + grab->shsurf == shsurf) { + shell_grab_end(grab); + free(grab); + } +} + +static void +ping_timer_destroy(struct shell_surface *shsurf) +{ + if (!shsurf || !shsurf->ping_timer) + return; + + if (shsurf->ping_timer->source) + wl_event_source_remove(shsurf->ping_timer->source); + + free(shsurf->ping_timer); + shsurf->ping_timer = NULL; +} + +static int +ping_timeout_handler(void *data) +{ + struct shell_surface *shsurf = data; + struct weston_seat *seat; + + /* Client is not responding */ + shsurf->unresponsive = 1; + + wl_list_for_each(seat, &shsurf->surface->compositor->seat_list, link) + if (seat->pointer->focus && + seat->pointer->focus->surface == shsurf->surface) + set_busy_cursor(shsurf, seat->pointer); + + return 1; +} + +static void +ping_handler(struct weston_surface *surface, uint32_t serial) +{ + struct shell_surface *shsurf = get_shell_surface(surface); + struct wl_event_loop *loop; + int ping_timeout = 200; + + if (!shsurf) + return; + if (!shsurf->resource) + return; + + if (shsurf->surface == shsurf->shell->grab_surface) + return; + + if (!shsurf->ping_timer) { + shsurf->ping_timer = malloc(sizeof *shsurf->ping_timer); + if (!shsurf->ping_timer) + return; + + shsurf->ping_timer->serial = serial; + loop = wl_display_get_event_loop(surface->compositor->wl_display); + shsurf->ping_timer->source = + wl_event_loop_add_timer(loop, ping_timeout_handler, shsurf); + wl_event_source_timer_update(shsurf->ping_timer->source, ping_timeout); + + if (shell_surface_is_wl_shell_surface(shsurf)) + wl_shell_surface_send_ping(shsurf->resource, serial); + else if (shell_surface_is_xdg_surface(shsurf)) + xdg_surface_send_ping(shsurf->resource, serial); + else if (shell_surface_is_xdg_popup(shsurf)) + xdg_popup_send_ping(shsurf->resource, serial); + } +} + +static void +handle_pointer_focus(struct wl_listener *listener, void *data) +{ + struct weston_pointer *pointer = data; + struct weston_view *view = pointer->focus; + struct weston_compositor *compositor; + struct shell_surface *shsurf; + uint32_t serial; + + if (!view) + return; + + compositor = view->surface->compositor; + shsurf = get_shell_surface(view->surface); + + if (shsurf && shsurf->unresponsive) { + set_busy_cursor(shsurf, pointer); + } else { + serial = wl_display_next_serial(compositor->wl_display); + ping_handler(view->surface, serial); + } +} + +static void +create_pointer_focus_listener(struct weston_seat *seat) +{ + struct wl_listener *listener; + + if (!seat->pointer) + return; + + listener = malloc(sizeof *listener); + listener->notify = handle_pointer_focus; + wl_signal_add(&seat->pointer->focus_signal, listener); +} + +static void +xdg_surface_set_transient_for(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *parent_resource) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + struct weston_surface *parent; + + if (parent_resource) + parent = wl_resource_get_user_data(parent_resource); + else + parent = NULL; + + shell_surface_set_parent(shsurf, parent); +} + +static void +surface_pong(struct shell_surface *shsurf, uint32_t serial) +{ + struct weston_compositor *ec = shsurf->surface->compositor; + struct weston_seat *seat; + + if (shsurf->ping_timer == NULL) + /* Just ignore unsolicited pong. */ + return; + + if (shsurf->ping_timer->serial == serial) { + shsurf->unresponsive = 0; + wl_list_for_each(seat, &ec->seat_list, link) { + if(seat->pointer) + end_busy_cursor(shsurf, seat->pointer); + } + ping_timer_destroy(shsurf); + } +} + +static void +shell_surface_pong(struct wl_client *client, struct wl_resource *resource, + uint32_t serial) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + + surface_pong(shsurf, serial); + +} + +static void +set_title(struct shell_surface *shsurf, const char *title) +{ + free(shsurf->title); + shsurf->title = strdup(title); +} + +static void +shell_surface_set_title(struct wl_client *client, + struct wl_resource *resource, const char *title) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + + set_title(shsurf, title); +} + +static void +shell_surface_set_class(struct wl_client *client, + struct wl_resource *resource, const char *class) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + + free(shsurf->class); + shsurf->class = strdup(class); +} + +static void +restore_output_mode(struct weston_output *output) +{ + if (output->current_mode != output->original_mode || + (int32_t)output->current_scale != output->original_scale) + weston_output_switch_mode(output, + output->original_mode, + output->original_scale, + WESTON_MODE_SWITCH_RESTORE_NATIVE); +} + +static void +restore_all_output_modes(struct weston_compositor *compositor) +{ + struct weston_output *output; + + wl_list_for_each(output, &compositor->output_list, link) + restore_output_mode(output); +} + +static int +get_output_panel_height(struct desktop_shell *shell, + struct weston_output *output) +{ + struct weston_view *view; + int panel_height = 0; + + if (!output) + return 0; + + wl_list_for_each(view, &shell->panel_layer.view_list, layer_link) { + if (view->surface->output == output) { + panel_height = view->surface->height; + break; + } + } + + return panel_height; +} + +/* The surface will be inserted into the list immediately after the link + * returned by this function (i.e. will be stacked immediately above the + * returned link). */ +static struct wl_list * +shell_surface_calculate_layer_link (struct shell_surface *shsurf) +{ + struct workspace *ws; + struct weston_view *parent; + + switch (shsurf->type) { + case SHELL_SURFACE_POPUP: + case SHELL_SURFACE_TOPLEVEL: + if (shsurf->state.fullscreen) { + return &shsurf->shell->fullscreen_layer.view_list; + } else if (shsurf->parent) { + /* Move the surface to its parent layer so + * that surfaces which are transient for + * fullscreen surfaces don't get hidden by the + * fullscreen surfaces. */ + + /* TODO: Handle a parent with multiple views */ + parent = get_default_view(shsurf->parent); + if (parent) + return parent->layer_link.prev; + } + break; + + case SHELL_SURFACE_XWAYLAND: + return &shsurf->shell->fullscreen_layer.view_list; + + case SHELL_SURFACE_NONE: + default: + /* Go to the fallback, below. */ + break; + } + + /* Move the surface to a normal workspace layer so that surfaces + * which were previously fullscreen or transient are no longer + * rendered on top. */ + ws = get_current_workspace(shsurf->shell); + return &ws->layer.view_list; +} + +static void +shell_surface_update_child_surface_layers (struct shell_surface *shsurf) +{ + struct shell_surface *child; + + /* Move the child layers to the same workspace as shsurf. They will be + * stacked above shsurf. */ + wl_list_for_each_reverse(child, &shsurf->children_list, children_link) { + if (shsurf->view->layer_link.prev != &child->view->layer_link) { + weston_view_geometry_dirty(child->view); + wl_list_remove(&child->view->layer_link); + wl_list_insert(shsurf->view->layer_link.prev, + &child->view->layer_link); + weston_view_geometry_dirty(child->view); + weston_surface_damage(child->surface); + + /* Recurse. We don’t expect this to recurse very far (if + * at all) because that would imply we have transient + * (or popup) children of transient surfaces, which + * would be unusual. */ + shell_surface_update_child_surface_layers(child); + } + } +} + +/* Update the surface’s layer. Mark both the old and new views as having dirty + * geometry to ensure the changes are redrawn. + * + * If any child surfaces exist and are mapped, ensure they’re in the same layer + * as this surface. */ +static void +shell_surface_update_layer(struct shell_surface *shsurf) +{ + struct wl_list *new_layer_link; + + new_layer_link = shell_surface_calculate_layer_link(shsurf); + + if (new_layer_link == &shsurf->view->layer_link) + return; + + weston_view_geometry_dirty(shsurf->view); + wl_list_remove(&shsurf->view->layer_link); + wl_list_insert(new_layer_link, &shsurf->view->layer_link); + weston_view_geometry_dirty(shsurf->view); + weston_surface_damage(shsurf->surface); + + shell_surface_update_child_surface_layers(shsurf); +} + +static void +shell_surface_set_parent(struct shell_surface *shsurf, + struct weston_surface *parent) +{ + shsurf->parent = parent; + + wl_list_remove(&shsurf->children_link); + wl_list_init(&shsurf->children_link); + + /* Insert into the parent surface’s child list. */ + if (parent != NULL) { + struct shell_surface *parent_shsurf = get_shell_surface(parent); + if (parent_shsurf != NULL) + wl_list_insert(&parent_shsurf->children_list, + &shsurf->children_link); + } +} + +static void +shell_surface_set_output(struct shell_surface *shsurf, + struct weston_output *output) +{ + struct weston_surface *es = shsurf->surface; + + /* get the default output, if the client set it as NULL + check whether the ouput is available */ + if (output) + shsurf->output = output; + else if (es->output) + shsurf->output = es->output; + else + shsurf->output = get_default_output(es->compositor); +} + +static void +surface_clear_next_states(struct shell_surface *shsurf) +{ + shsurf->next_state.maximized = false; + shsurf->next_state.fullscreen = false; + + if ((shsurf->next_state.maximized != shsurf->state.maximized) || + (shsurf->next_state.fullscreen != shsurf->state.fullscreen)) + shsurf->state_changed = true; +} + +static void +set_toplevel(struct shell_surface *shsurf) +{ + shell_surface_set_parent(shsurf, NULL); + surface_clear_next_states(shsurf); + shsurf->type = SHELL_SURFACE_TOPLEVEL; + + /* The layer_link is updated in set_surface_type(), + * called from configure. */ +} + +static void +shell_surface_set_toplevel(struct wl_client *client, + struct wl_resource *resource) +{ + struct shell_surface *surface = wl_resource_get_user_data(resource); + + set_toplevel(surface); +} + +static void +set_transient(struct shell_surface *shsurf, + struct weston_surface *parent, int x, int y, uint32_t flags) +{ + assert(parent != NULL); + + shell_surface_set_parent(shsurf, parent); + + surface_clear_next_states(shsurf); + + shsurf->transient.x = x; + shsurf->transient.y = y; + shsurf->transient.flags = flags; + + shsurf->next_state.relative = true; + shsurf->state_changed = true; + shsurf->type = SHELL_SURFACE_TOPLEVEL; + + /* The layer_link is updated in set_surface_type(), + * called from configure. */ +} + +static void +shell_surface_set_transient(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *parent_resource, + int x, int y, uint32_t flags) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + struct weston_surface *parent = + wl_resource_get_user_data(parent_resource); + + set_transient(shsurf, parent, x, y, flags); +} + +static void +set_fullscreen(struct shell_surface *shsurf, + uint32_t method, + uint32_t framerate, + struct weston_output *output) +{ + shell_surface_set_output(shsurf, output); + + shsurf->fullscreen_output = shsurf->output; + shsurf->fullscreen.type = method; + shsurf->fullscreen.framerate = framerate; + + shsurf->next_state.fullscreen = true; + shsurf->state_changed = true; + shsurf->type = SHELL_SURFACE_TOPLEVEL; + + shsurf->client->send_configure(shsurf->surface, 0, + shsurf->output->width, + shsurf->output->height); + + /* The layer_link is updated in set_surface_type(), + * called from configure. */ +} + +static void +weston_view_set_initial_position(struct weston_view *view, + struct desktop_shell *shell); + +static void +unset_fullscreen(struct shell_surface *shsurf) +{ + /* Unset the fullscreen output, driver configuration and transforms. */ + if (shsurf->fullscreen.type == WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER && + shell_surface_is_top_fullscreen(shsurf)) { + restore_output_mode(shsurf->fullscreen_output); + } + shsurf->fullscreen_output = NULL; + + shsurf->fullscreen.type = WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT; + shsurf->fullscreen.framerate = 0; + + wl_list_remove(&shsurf->fullscreen.transform.link); + wl_list_init(&shsurf->fullscreen.transform.link); + + if (shsurf->fullscreen.black_view) + weston_surface_destroy(shsurf->fullscreen.black_view->surface); + shsurf->fullscreen.black_view = NULL; + + if (shsurf->saved_position_valid) + weston_view_set_position(shsurf->view, + shsurf->saved_x, shsurf->saved_y); + else + weston_view_set_initial_position(shsurf->view, shsurf->shell); + + if (shsurf->saved_rotation_valid) { + wl_list_insert(&shsurf->view->geometry.transformation_list, + &shsurf->rotation.transform.link); + shsurf->saved_rotation_valid = false; + } + + /* Layer is updated in set_surface_type(). */ +} + +static void +shell_surface_set_fullscreen(struct wl_client *client, + struct wl_resource *resource, + uint32_t method, + uint32_t framerate, + struct wl_resource *output_resource) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + struct weston_output *output; + + if (output_resource) + output = wl_resource_get_user_data(output_resource); + else + output = NULL; + + shell_surface_set_parent(shsurf, NULL); + + surface_clear_next_states(shsurf); + set_fullscreen(shsurf, method, framerate, output); +} + +static void +set_popup(struct shell_surface *shsurf, + struct weston_surface *parent, + struct weston_seat *seat, + uint32_t serial, + int32_t x, + int32_t y) +{ + assert(parent != NULL); + + shsurf->popup.shseat = get_shell_seat(seat); + shsurf->popup.serial = serial; + shsurf->popup.x = x; + shsurf->popup.y = y; + + shsurf->type = SHELL_SURFACE_POPUP; +} + +static void +shell_surface_set_popup(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat_resource, + uint32_t serial, + struct wl_resource *parent_resource, + int32_t x, int32_t y, uint32_t flags) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + struct weston_surface *parent = + wl_resource_get_user_data(parent_resource); + + shell_surface_set_parent(shsurf, parent); + + surface_clear_next_states(shsurf); + set_popup(shsurf, + parent, + wl_resource_get_user_data(seat_resource), + serial, x, y); +} + +static void +set_maximized(struct shell_surface *shsurf, + struct weston_output *output) +{ + struct desktop_shell *shell; + uint32_t edges = 0, panel_height = 0; + + shell_surface_set_output(shsurf, output); + + shell = shell_surface_get_shell(shsurf); + panel_height = get_output_panel_height(shell, shsurf->output); + edges = WL_SHELL_SURFACE_RESIZE_TOP | WL_SHELL_SURFACE_RESIZE_LEFT; + + shsurf->client->send_configure(shsurf->surface, edges, + shsurf->output->width, + shsurf->output->height - panel_height); + + shsurf->next_state.maximized = true; + shsurf->state_changed = true; + shsurf->type = SHELL_SURFACE_TOPLEVEL; +} + +static void +unset_maximized(struct shell_surface *shsurf) +{ + /* undo all maximized things here */ + shsurf->output = get_default_output(shsurf->surface->compositor); + + if (shsurf->saved_position_valid) + weston_view_set_position(shsurf->view, + shsurf->saved_x, shsurf->saved_y); + else + weston_view_set_initial_position(shsurf->view, shsurf->shell); + + if (shsurf->saved_rotation_valid) { + wl_list_insert(&shsurf->view->geometry.transformation_list, + &shsurf->rotation.transform.link); + shsurf->saved_rotation_valid = false; + } + + /* Layer is updated in set_surface_type(). */ +} + +static void +shell_surface_set_maximized(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output_resource) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + struct weston_output *output; + + if (output_resource) + output = wl_resource_get_user_data(output_resource); + else + output = NULL; + + shell_surface_set_parent(shsurf, NULL); + + surface_clear_next_states(shsurf); + set_maximized(shsurf, output); +} + +/* This is only ever called from set_surface_type(), so there’s no need to + * update layer_links here, since they’ll be updated when we return. */ +static int +reset_surface_type(struct shell_surface *surface) +{ + if (surface->state.fullscreen) + unset_fullscreen(surface); + if (surface->state.maximized) + unset_maximized(surface); + + return 0; +} + +static void +set_full_output(struct shell_surface *shsurf) +{ + shsurf->saved_x = shsurf->view->geometry.x; + shsurf->saved_y = shsurf->view->geometry.y; + shsurf->saved_width = shsurf->surface->width; + shsurf->saved_height = shsurf->surface->height; + shsurf->saved_size_valid = true; + shsurf->saved_position_valid = true; + + if (!wl_list_empty(&shsurf->rotation.transform.link)) { + wl_list_remove(&shsurf->rotation.transform.link); + wl_list_init(&shsurf->rotation.transform.link); + weston_view_geometry_dirty(shsurf->view); + shsurf->saved_rotation_valid = true; + } +} + +static void +set_surface_type(struct shell_surface *shsurf) +{ + struct weston_surface *pes = shsurf->parent; + struct weston_view *pev = get_default_view(pes); + + reset_surface_type(shsurf); + + shsurf->state = shsurf->next_state; + shsurf->state_changed = false; + + switch (shsurf->type) { + case SHELL_SURFACE_TOPLEVEL: + if (shsurf->state.maximized || shsurf->state.fullscreen) { + set_full_output(shsurf); + } else if (shsurf->state.relative && pev) { + weston_view_set_position(shsurf->view, + pev->geometry.x + shsurf->transient.x, + pev->geometry.y + shsurf->transient.y); + } + break; + + case SHELL_SURFACE_XWAYLAND: + weston_view_set_position(shsurf->view, shsurf->transient.x, + shsurf->transient.y); + break; + + case SHELL_SURFACE_POPUP: + case SHELL_SURFACE_NONE: + default: + break; + } + + /* Update the surface’s layer. */ + shell_surface_update_layer(shsurf); +} + +static struct desktop_shell * +shell_surface_get_shell(struct shell_surface *shsurf) +{ + return shsurf->shell; +} + +static void +black_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy); + +static struct weston_view * +create_black_surface(struct weston_compositor *ec, + struct weston_surface *fs_surface, + float x, float y, int w, int h) +{ + struct weston_surface *surface = NULL; + struct weston_view *view; + + surface = weston_surface_create(ec); + if (surface == NULL) { + weston_log("no memory\n"); + return NULL; + } + view = weston_view_create(surface); + if (surface == NULL) { + weston_log("no memory\n"); + weston_surface_destroy(surface); + return NULL; + } + + surface->configure = black_surface_configure; + surface->configure_private = fs_surface; + weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1); + pixman_region32_fini(&surface->opaque); + pixman_region32_init_rect(&surface->opaque, 0, 0, w, h); + pixman_region32_fini(&surface->input); + pixman_region32_init_rect(&surface->input, 0, 0, w, h); + + weston_surface_set_size(surface, w, h); + weston_view_set_position(view, x, y); + + return view; +} + +static void +shell_ensure_fullscreen_black_view(struct shell_surface *shsurf) +{ + struct weston_output *output = shsurf->fullscreen_output; + + assert(shsurf->state.fullscreen); + + if (!shsurf->fullscreen.black_view) + shsurf->fullscreen.black_view = + create_black_surface(shsurf->surface->compositor, + shsurf->surface, + output->x, output->y, + output->width, + output->height); + + weston_view_geometry_dirty(shsurf->fullscreen.black_view); + wl_list_remove(&shsurf->fullscreen.black_view->layer_link); + wl_list_insert(&shsurf->view->layer_link, + &shsurf->fullscreen.black_view->layer_link); + weston_view_geometry_dirty(shsurf->fullscreen.black_view); + weston_surface_damage(shsurf->surface); +} + +/* Create black surface and append it to the associated fullscreen surface. + * Handle size dismatch and positioning according to the method. */ +static void +shell_configure_fullscreen(struct shell_surface *shsurf) +{ + struct weston_output *output = shsurf->fullscreen_output; + struct weston_surface *surface = shsurf->surface; + struct weston_matrix *matrix; + float scale, output_aspect, surface_aspect, x, y; + int32_t surf_x, surf_y, surf_width, surf_height; + + if (shsurf->fullscreen.type != WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER) + restore_output_mode(output); + + shell_ensure_fullscreen_black_view(shsurf); + + surface_subsurfaces_boundingbox(shsurf->surface, &surf_x, &surf_y, + &surf_width, &surf_height); + + switch (shsurf->fullscreen.type) { + case WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT: + if (surface->buffer_ref.buffer) + center_on_output(shsurf->view, shsurf->fullscreen_output); + break; + case WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE: + /* 1:1 mapping between surface and output dimensions */ + if (output->width == surf_width && + output->height == surf_height) { + weston_view_set_position(shsurf->view, + output->x - surf_x, + output->y - surf_y); + break; + } + + matrix = &shsurf->fullscreen.transform.matrix; + weston_matrix_init(matrix); + + output_aspect = (float) output->width / + (float) output->height; + /* XXX: Use surf_width and surf_height here? */ + surface_aspect = (float) surface->width / + (float) surface->height; + if (output_aspect < surface_aspect) + scale = (float) output->width / + (float) surf_width; + else + scale = (float) output->height / + (float) surf_height; + + weston_matrix_scale(matrix, scale, scale, 1); + wl_list_remove(&shsurf->fullscreen.transform.link); + wl_list_insert(&shsurf->view->geometry.transformation_list, + &shsurf->fullscreen.transform.link); + x = output->x + (output->width - surf_width * scale) / 2 - surf_x; + y = output->y + (output->height - surf_height * scale) / 2 - surf_y; + weston_view_set_position(shsurf->view, x, y); + + break; + case WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER: + if (shell_surface_is_top_fullscreen(shsurf)) { + struct weston_mode mode = {0, + surf_width * surface->buffer_viewport.scale, + surf_height * surface->buffer_viewport.scale, + shsurf->fullscreen.framerate}; + + if (weston_output_switch_mode(output, &mode, surface->buffer_viewport.scale, + WESTON_MODE_SWITCH_SET_TEMPORARY) == 0) { + weston_view_set_position(shsurf->view, + output->x - surf_x, + output->y - surf_y); + shsurf->fullscreen.black_view->surface->width = output->width; + shsurf->fullscreen.black_view->surface->height = output->height; + weston_view_set_position(shsurf->fullscreen.black_view, + output->x - surf_x, + output->y - surf_y); + break; + } else { + restore_output_mode(output); + center_on_output(shsurf->view, output); + } + } + break; + case WL_SHELL_SURFACE_FULLSCREEN_METHOD_FILL: + center_on_output(shsurf->view, output); + break; + default: + break; + } +} + +static void +shell_map_fullscreen(struct shell_surface *shsurf) +{ + shell_configure_fullscreen(shsurf); +} + +static void +set_xwayland(struct shell_surface *shsurf, int x, int y, uint32_t flags) +{ + /* XXX: using the same fields for transient type */ + surface_clear_next_states(shsurf); + shsurf->transient.x = x; + shsurf->transient.y = y; + shsurf->transient.flags = flags; + + shell_surface_set_parent(shsurf, NULL); + + shsurf->type = SHELL_SURFACE_XWAYLAND; + shsurf->state_changed = true; +} + +static const struct weston_pointer_grab_interface popup_grab_interface; + +static void +destroy_shell_seat(struct wl_listener *listener, void *data) +{ + struct shell_seat *shseat = + container_of(listener, + struct shell_seat, seat_destroy_listener); + struct shell_surface *shsurf, *prev = NULL; + + if (shseat->popup_grab.grab.interface == &popup_grab_interface) { + weston_pointer_end_grab(shseat->popup_grab.grab.pointer); + shseat->popup_grab.client = NULL; + + wl_list_for_each(shsurf, &shseat->popup_grab.surfaces_list, popup.grab_link) { + shsurf->popup.shseat = NULL; + if (prev) { + wl_list_init(&prev->popup.grab_link); + } + prev = shsurf; + } + wl_list_init(&prev->popup.grab_link); + } + + wl_list_remove(&shseat->seat_destroy_listener.link); + free(shseat); +} + +static struct shell_seat * +create_shell_seat(struct weston_seat *seat) +{ + struct shell_seat *shseat; + + shseat = calloc(1, sizeof *shseat); + if (!shseat) { + weston_log("no memory to allocate shell seat\n"); + return NULL; + } + + shseat->seat = seat; + wl_list_init(&shseat->popup_grab.surfaces_list); + + shseat->seat_destroy_listener.notify = destroy_shell_seat; + wl_signal_add(&seat->destroy_signal, + &shseat->seat_destroy_listener); + + return shseat; +} + +static struct shell_seat * +get_shell_seat(struct weston_seat *seat) +{ + struct wl_listener *listener; + + listener = wl_signal_get(&seat->destroy_signal, destroy_shell_seat); + if (listener == NULL) + return create_shell_seat(seat); + + return container_of(listener, + struct shell_seat, seat_destroy_listener); +} + +static void +popup_grab_focus(struct weston_pointer_grab *grab) +{ + struct weston_pointer *pointer = grab->pointer; + struct weston_view *view; + struct shell_seat *shseat = + container_of(grab, struct shell_seat, popup_grab.grab); + struct wl_client *client = shseat->popup_grab.client; + wl_fixed_t sx, sy; + + view = weston_compositor_pick_view(pointer->seat->compositor, + pointer->x, pointer->y, + &sx, &sy); + + if (view && view->surface->resource && + wl_resource_get_client(view->surface->resource) == client) { + weston_pointer_set_focus(pointer, view, sx, sy); + } else { + weston_pointer_set_focus(pointer, NULL, + wl_fixed_from_int(0), + wl_fixed_from_int(0)); + } +} + +static void +popup_grab_motion(struct weston_pointer_grab *grab, uint32_t time, + wl_fixed_t x, wl_fixed_t y) +{ + struct weston_pointer *pointer = grab->pointer; + struct wl_resource *resource; + wl_fixed_t sx, sy; + + weston_pointer_move(pointer, x, y); + + wl_resource_for_each(resource, &pointer->focus_resource_list) { + weston_view_from_global_fixed(pointer->focus, + pointer->x, pointer->y, + &sx, &sy); + wl_pointer_send_motion(resource, time, sx, sy); + } +} + +static void +popup_grab_button(struct weston_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state_w) +{ + struct wl_resource *resource; + struct shell_seat *shseat = + container_of(grab, struct shell_seat, popup_grab.grab); + struct wl_display *display = shseat->seat->compositor->wl_display; + enum wl_pointer_button_state state = state_w; + uint32_t serial; + struct wl_list *resource_list; + + resource_list = &grab->pointer->focus_resource_list; + if (!wl_list_empty(resource_list)) { + serial = wl_display_get_serial(display); + wl_resource_for_each(resource, resource_list) { + wl_pointer_send_button(resource, serial, + time, button, state); + } + } else if (state == WL_POINTER_BUTTON_STATE_RELEASED && + (shseat->popup_grab.initial_up || + time - shseat->seat->pointer->grab_time > 500)) { + popup_grab_end(grab->pointer); + } + + if (state == WL_POINTER_BUTTON_STATE_RELEASED) + shseat->popup_grab.initial_up = 1; +} + +static void +popup_grab_cancel(struct weston_pointer_grab *grab) +{ + popup_grab_end(grab->pointer); +} + +static const struct weston_pointer_grab_interface popup_grab_interface = { + popup_grab_focus, + popup_grab_motion, + popup_grab_button, + popup_grab_cancel, +}; + +static void +shell_surface_send_popup_done(struct shell_surface *shsurf) +{ + if (shell_surface_is_wl_shell_surface(shsurf)) + wl_shell_surface_send_popup_done(shsurf->resource); + else if (shell_surface_is_xdg_popup(shsurf)) + xdg_popup_send_popup_done(shsurf->resource, + shsurf->popup.serial); +} + +static void +popup_grab_end(struct weston_pointer *pointer) +{ + struct weston_pointer_grab *grab = pointer->grab; + struct shell_seat *shseat = + container_of(grab, struct shell_seat, popup_grab.grab); + struct shell_surface *shsurf; + struct shell_surface *prev = NULL; + + if (pointer->grab->interface == &popup_grab_interface) { + weston_pointer_end_grab(grab->pointer); + shseat->popup_grab.client = NULL; + shseat->popup_grab.grab.interface = NULL; + assert(!wl_list_empty(&shseat->popup_grab.surfaces_list)); + /* Send the popup_done event to all the popups open */ + wl_list_for_each(shsurf, &shseat->popup_grab.surfaces_list, popup.grab_link) { + shell_surface_send_popup_done(shsurf); + shsurf->popup.shseat = NULL; + if (prev) { + wl_list_init(&prev->popup.grab_link); + } + prev = shsurf; + } + wl_list_init(&prev->popup.grab_link); + wl_list_init(&shseat->popup_grab.surfaces_list); + } +} + +static void +add_popup_grab(struct shell_surface *shsurf, struct shell_seat *shseat) +{ + struct weston_seat *seat = shseat->seat; + + if (wl_list_empty(&shseat->popup_grab.surfaces_list)) { + shseat->popup_grab.client = wl_resource_get_client(shsurf->resource); + shseat->popup_grab.grab.interface = &popup_grab_interface; + /* We must make sure here that this popup was opened after + * a mouse press, and not just by moving around with other + * popups already open. */ + if (shseat->seat->pointer->button_count > 0) + shseat->popup_grab.initial_up = 0; + + wl_list_insert(&shseat->popup_grab.surfaces_list, &shsurf->popup.grab_link); + weston_pointer_start_grab(seat->pointer, &shseat->popup_grab.grab); + } else { + wl_list_insert(&shseat->popup_grab.surfaces_list, &shsurf->popup.grab_link); + } +} + +static void +remove_popup_grab(struct shell_surface *shsurf) +{ + struct shell_seat *shseat = shsurf->popup.shseat; + + wl_list_remove(&shsurf->popup.grab_link); + wl_list_init(&shsurf->popup.grab_link); + if (wl_list_empty(&shseat->popup_grab.surfaces_list)) { + weston_pointer_end_grab(shseat->popup_grab.grab.pointer); + shseat->popup_grab.grab.interface = NULL; + } +} + +static void +shell_map_popup(struct shell_surface *shsurf) +{ + struct shell_seat *shseat = shsurf->popup.shseat; + struct weston_view *parent_view = get_default_view(shsurf->parent); + + shsurf->surface->output = parent_view->output; + shsurf->view->output = parent_view->output; + + weston_view_set_transform_parent(shsurf->view, parent_view); + weston_view_set_position(shsurf->view, shsurf->popup.x, shsurf->popup.y); + weston_view_update_transform(shsurf->view); + + if (shseat->seat->pointer->grab_serial == shsurf->popup.serial) { + add_popup_grab(shsurf, shseat); + } else { + shell_surface_send_popup_done(shsurf); + shseat->popup_grab.client = NULL; + } +} + +static const struct wl_shell_surface_interface shell_surface_implementation = { + shell_surface_pong, + shell_surface_move, + shell_surface_resize, + shell_surface_set_toplevel, + shell_surface_set_transient, + shell_surface_set_fullscreen, + shell_surface_set_popup, + shell_surface_set_maximized, + shell_surface_set_title, + shell_surface_set_class +}; + +static void +destroy_shell_surface(struct shell_surface *shsurf) +{ + struct shell_surface *child, *next; + + wl_signal_emit(&shsurf->destroy_signal, shsurf); + + if (!wl_list_empty(&shsurf->popup.grab_link)) { + remove_popup_grab(shsurf); + } + + if (shsurf->fullscreen.type == WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER && + shell_surface_is_top_fullscreen(shsurf)) + restore_output_mode (shsurf->fullscreen_output); + + if (shsurf->fullscreen.black_view) + weston_surface_destroy(shsurf->fullscreen.black_view->surface); + + /* As destroy_resource() use wl_list_for_each_safe(), + * we can always remove the listener. + */ + wl_list_remove(&shsurf->surface_destroy_listener.link); + shsurf->surface->configure = NULL; + ping_timer_destroy(shsurf); + free(shsurf->title); + + weston_view_destroy(shsurf->view); + + wl_list_remove(&shsurf->children_link); + wl_list_for_each_safe(child, next, &shsurf->children_list, children_link) { + wl_list_remove(&child->children_link); + child->parent = NULL; + } + + wl_list_remove(&shsurf->link); + free(shsurf); +} + +static void +shell_destroy_shell_surface(struct wl_resource *resource) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + + destroy_shell_surface(shsurf); +} + +static void +shell_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct shell_surface *shsurf = container_of(listener, + struct shell_surface, + surface_destroy_listener); + + if (shsurf->resource) + wl_resource_destroy(shsurf->resource); + else + destroy_shell_surface(shsurf); +} + +static void +shell_surface_configure(struct weston_surface *, int32_t, int32_t); +static void +shell_surface_output_destroyed(struct weston_surface *); + +struct shell_surface * +get_shell_surface(struct weston_surface *surface) +{ + if (surface->configure == shell_surface_configure) + return surface->configure_private; + else + return NULL; +} + +static struct shell_surface * +create_common_surface(void *shell, struct weston_surface *surface, + const struct weston_shell_client *client) +{ + struct shell_surface *shsurf; + + if (surface->configure) { + weston_log("surface->configure already set\n"); + return NULL; + } + + shsurf = calloc(1, sizeof *shsurf); + if (!shsurf) { + weston_log("no memory to allocate shell surface\n"); + return NULL; + } + + shsurf->view = weston_view_create(surface); + if (!shsurf->view) { + weston_log("no memory to allocate shell surface\n"); + free(shsurf); + return NULL; + } + + surface->configure = shell_surface_configure; + surface->configure_private = shsurf; + surface->output_destroyed = shell_surface_output_destroyed; + + shsurf->shell = (struct desktop_shell *) shell; + shsurf->unresponsive = 0; + shsurf->saved_position_valid = false; + shsurf->saved_size_valid = false; + shsurf->saved_rotation_valid = false; + shsurf->surface = surface; + shsurf->fullscreen.type = WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT; + shsurf->fullscreen.framerate = 0; + shsurf->fullscreen.black_view = NULL; + shsurf->ping_timer = NULL; + wl_list_init(&shsurf->fullscreen.transform.link); + + wl_signal_init(&shsurf->destroy_signal); + shsurf->surface_destroy_listener.notify = shell_handle_surface_destroy; + wl_signal_add(&surface->destroy_signal, + &shsurf->surface_destroy_listener); + + /* init link so its safe to always remove it in destroy_shell_surface */ + wl_list_init(&shsurf->link); + wl_list_init(&shsurf->popup.grab_link); + + /* empty when not in use */ + wl_list_init(&shsurf->rotation.transform.link); + weston_matrix_init(&shsurf->rotation.rotation); + + wl_list_init(&shsurf->workspace_transform.link); + + wl_list_init(&shsurf->children_link); + wl_list_init(&shsurf->children_list); + shsurf->parent = NULL; + + shsurf->type = SHELL_SURFACE_NONE; + + shsurf->client = client; + + return shsurf; +} + +static struct shell_surface * +create_shell_surface(void *shell, struct weston_surface *surface, + const struct weston_shell_client *client) +{ + return create_common_surface(shell, surface, client); +} + +static struct weston_view * +get_primary_view(void *shell, struct shell_surface *shsurf) +{ + return shsurf->view; +} + +static void +shell_get_shell_surface(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface_resource) +{ + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + struct desktop_shell *shell = wl_resource_get_user_data(resource); + struct shell_surface *shsurf; + + if (get_shell_surface(surface)) { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "desktop_shell::get_shell_surface already requested"); + return; + } + + shsurf = create_shell_surface(shell, surface, &shell_client); + if (!shsurf) { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "surface->configure already set"); + return; + } + + shsurf->resource = + wl_resource_create(client, + &wl_shell_surface_interface, 1, id); + wl_resource_set_implementation(shsurf->resource, + &shell_surface_implementation, + shsurf, shell_destroy_shell_surface); +} + +static bool +shell_surface_is_wl_shell_surface(struct shell_surface *shsurf) +{ + return wl_resource_instance_of(shsurf->resource, + &wl_shell_surface_interface, + &shell_surface_implementation); +} + +static const struct wl_shell_interface shell_implementation = { + shell_get_shell_surface +}; + +/**************************** + * xdg-shell implementation */ + +static void +xdg_surface_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +xdg_surface_pong(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + + surface_pong(shsurf, serial); +} + +static void +xdg_surface_set_app_id(struct wl_client *client, + struct wl_resource *resource, + const char *app_id) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + + free(shsurf->class); + shsurf->class = strdup(app_id); +} + +static void +xdg_surface_set_title(struct wl_client *client, + struct wl_resource *resource, const char *title) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + + set_title(shsurf, title); +} + +static void +xdg_surface_move(struct wl_client *client, struct wl_resource *resource, + struct wl_resource *seat_resource, uint32_t serial) +{ + common_surface_move(resource, seat_resource, serial); +} + +static void +xdg_surface_resize(struct wl_client *client, struct wl_resource *resource, + struct wl_resource *seat_resource, uint32_t serial, + uint32_t edges) +{ + common_surface_resize(resource, seat_resource, serial, edges); +} + +static void +xdg_surface_set_output(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output_resource) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + struct weston_output *output; + + if (output_resource) + output = wl_resource_get_user_data(output_resource); + else + output = NULL; + + shsurf->recommended_output = output; +} + +static void +xdg_surface_set_fullscreen(struct wl_client *client, + struct wl_resource *resource) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + + if (shsurf->type != SHELL_SURFACE_TOPLEVEL) + return; + + if (!shsurf->next_state.fullscreen) + set_fullscreen(shsurf, + WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, + 0, shsurf->recommended_output); +} + +static void +xdg_surface_unset_fullscreen(struct wl_client *client, + struct wl_resource *resource) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + int32_t width, height; + + if (shsurf->type != SHELL_SURFACE_TOPLEVEL) + return; + + if (!shsurf->next_state.fullscreen) + return; + + shsurf->next_state.fullscreen = false; + shsurf->state_changed = true; + + if (shsurf->saved_size_valid) { + width = shsurf->saved_width; + height = shsurf->saved_height; + shsurf->saved_size_valid = false; + } else { + width = shsurf->surface->width; + height = shsurf->surface->height; + } + + shsurf->client->send_configure(shsurf->surface, 0, width, height); +} + +static void +xdg_surface_set_maximized(struct wl_client *client, + struct wl_resource *resource) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + + if (shsurf->type != SHELL_SURFACE_TOPLEVEL) + return; + + if (!shsurf->next_state.maximized) + set_maximized(shsurf, NULL); +} + +static void +xdg_surface_unset_maximized(struct wl_client *client, + struct wl_resource *resource) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + int32_t width, height; + + if (shsurf->type != SHELL_SURFACE_TOPLEVEL) + return; + + if (!shsurf->next_state.maximized) + return; + + shsurf->next_state.maximized = false; + shsurf->state_changed = true; + + if (shsurf->saved_size_valid) { + width = shsurf->saved_width; + height = shsurf->saved_height; + shsurf->saved_size_valid = false; + } else { + width = shsurf->surface->width; + height = shsurf->surface->height; + } + + shsurf->client->send_configure(shsurf->surface, 0, width, height); +} + +static const struct xdg_surface_interface xdg_surface_implementation = { + xdg_surface_destroy, + xdg_surface_set_transient_for, + xdg_surface_set_title, + xdg_surface_set_app_id, + xdg_surface_pong, + xdg_surface_move, + xdg_surface_resize, + xdg_surface_set_output, + xdg_surface_set_fullscreen, + xdg_surface_unset_fullscreen, + xdg_surface_set_maximized, + xdg_surface_unset_maximized, + NULL /* set_minimized */ +}; + +static void +xdg_send_configure(struct weston_surface *surface, + uint32_t edges, int32_t width, int32_t height) +{ + struct shell_surface *shsurf = get_shell_surface(surface); + + xdg_surface_send_configure(shsurf->resource, edges, width, height); +} + +static const struct weston_shell_client xdg_client = { + xdg_send_configure +}; + +static void +xdg_use_unstable_version(struct wl_client *client, + struct wl_resource *resource, + int32_t version) +{ + if (version > 1) { + wl_resource_post_error(resource, + 1, + "xdg-shell:: version not implemented yet."); + return; + } +} + +static struct shell_surface * +create_xdg_surface(void *shell, struct weston_surface *surface, + const struct weston_shell_client *client) +{ + struct shell_surface *shsurf; + + shsurf = create_common_surface(shell, surface, client); + shsurf->type = SHELL_SURFACE_TOPLEVEL; + + return shsurf; +} + +static void +xdg_get_xdg_surface(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface_resource) +{ + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + struct desktop_shell *shell = wl_resource_get_user_data(resource); + struct shell_surface *shsurf; + + if (get_shell_surface(surface)) { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "desktop_shell::get_shell_surface already requested"); + return; + } + + shsurf = create_xdg_surface(shell, surface, &xdg_client); + if (!shsurf) { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "surface->configure already set"); + return; + } + + shsurf->resource = + wl_resource_create(client, + &xdg_surface_interface, 1, id); + wl_resource_set_implementation(shsurf->resource, + &xdg_surface_implementation, + shsurf, shell_destroy_shell_surface); +} + +static bool +shell_surface_is_xdg_surface(struct shell_surface *shsurf) +{ + return wl_resource_instance_of(shsurf->resource, + &xdg_surface_interface, + &xdg_surface_implementation); +} + +/* xdg-popup implementation */ + +static void +xdg_popup_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +xdg_popup_pong(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial) +{ + struct shell_surface *shsurf = wl_resource_get_user_data(resource); + + surface_pong(shsurf, serial); +} + +static const struct xdg_popup_interface xdg_popup_implementation = { + xdg_popup_destroy, + xdg_popup_pong +}; + +static void +xdg_popup_send_configure(struct weston_surface *surface, + uint32_t edges, int32_t width, int32_t height) +{ +} + +static const struct weston_shell_client xdg_popup_client = { + xdg_popup_send_configure +}; + +static struct shell_surface * +create_xdg_popup(void *shell, struct weston_surface *surface, + const struct weston_shell_client *client, + struct weston_surface *parent, + struct shell_seat *seat, + uint32_t serial, + int32_t x, int32_t y) +{ + struct shell_surface *shsurf; + + shsurf = create_common_surface(shell, surface, client); + shsurf->type = SHELL_SURFACE_POPUP; + shsurf->popup.shseat = seat; + shsurf->popup.serial = serial; + shsurf->popup.x = x; + shsurf->popup.y = y; + shell_surface_set_parent(shsurf, parent); + + return shsurf; +} + +static void +xdg_get_xdg_popup(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface_resource, + struct wl_resource *parent_resource, + struct wl_resource *seat_resource, + uint32_t serial, + int32_t x, int32_t y, uint32_t flags) +{ + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + struct desktop_shell *shell = wl_resource_get_user_data(resource); + struct shell_surface *shsurf; + struct weston_surface *parent; + struct shell_seat *seat; + + if (get_shell_surface(surface)) { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "desktop_shell::get_shell_surface already requested"); + return; + } + + if (!parent_resource) { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "xdg_shell::get_xdg_popup requires a parent shell surface"); + } + + parent = wl_resource_get_user_data(parent_resource); + seat = get_shell_seat(wl_resource_get_user_data(seat_resource));; + + shsurf = create_xdg_popup(shell, surface, &xdg_popup_client, + parent, seat, serial, x, y); + if (!shsurf) { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "surface->configure already set"); + return; + } + + shsurf->resource = + wl_resource_create(client, + &xdg_popup_interface, 1, id); + wl_resource_set_implementation(shsurf->resource, + &xdg_popup_implementation, + shsurf, shell_destroy_shell_surface); +} + +static bool +shell_surface_is_xdg_popup(struct shell_surface *shsurf) +{ + return wl_resource_instance_of(shsurf->resource, + &xdg_popup_interface, + &xdg_popup_implementation); +} + +static const struct xdg_shell_interface xdg_implementation = { + xdg_use_unstable_version, + xdg_get_xdg_surface, + xdg_get_xdg_popup +}; + +static int +xdg_shell_unversioned_dispatch(const void *implementation, + void *_target, uint32_t opcode, + const struct wl_message *message, + union wl_argument *args) +{ + struct wl_resource *resource = _target; + struct desktop_shell *shell = wl_resource_get_user_data(resource); + + if (opcode != 0) { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "must call use_unstable_version first"); + return 0; + } + +#define XDG_SERVER_VERSION 1 + + static_assert(XDG_SERVER_VERSION == XDG_SHELL_VERSION_CURRENT, + "shell implementation doesn't match protocol version"); + + if (args[0].i != XDG_SERVER_VERSION) { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "incompatible version, server is %d " + "client wants %d", + XDG_SERVER_VERSION, args[0].i); + return 0; + } + + wl_resource_set_implementation(resource, &xdg_implementation, + shell, NULL); + + return 1; +} + +/* end of xdg-shell implementation */ +/***********************************/ + +static void +shell_fade(struct desktop_shell *shell, enum fade_type type); + +static int +screensaver_timeout(void *data) +{ + struct desktop_shell *shell = data; + + shell_fade(shell, FADE_OUT); + + return 1; +} + +static void +handle_screensaver_sigchld(struct weston_process *proc, int status) +{ + struct desktop_shell *shell = + container_of(proc, struct desktop_shell, screensaver.process); + + proc->pid = 0; + + if (shell->locked) + weston_compositor_sleep(shell->compositor); +} + +static void +launch_screensaver(struct desktop_shell *shell) +{ + if (shell->screensaver.binding) + return; + + if (!shell->screensaver.path) { + weston_compositor_sleep(shell->compositor); + return; + } + + if (shell->screensaver.process.pid != 0) { + weston_log("old screensaver still running\n"); + return; + } + + weston_client_launch(shell->compositor, + &shell->screensaver.process, + shell->screensaver.path, + handle_screensaver_sigchld); +} + +static void +terminate_screensaver(struct desktop_shell *shell) +{ + if (shell->screensaver.process.pid == 0) + return; + + kill(shell->screensaver.process.pid, SIGTERM); +} + +static void +configure_static_view(struct weston_view *ev, struct weston_layer *layer) +{ + struct weston_view *v, *next; + + wl_list_for_each_safe(v, next, &layer->view_list, layer_link) { + if (v->output == ev->output && v != ev) { + weston_view_unmap(v); + v->surface->configure = NULL; + } + } + + weston_view_set_position(ev, ev->output->x, ev->output->y); + + if (wl_list_empty(&ev->layer_link)) { + wl_list_insert(&layer->view_list, &ev->layer_link); + weston_compositor_schedule_repaint(ev->surface->compositor); + } +} + +static void +background_configure(struct weston_surface *es, int32_t sx, int32_t sy) +{ + struct desktop_shell *shell = es->configure_private; + struct weston_view *view; + + view = container_of(es->views.next, struct weston_view, surface_link); + + configure_static_view(view, &shell->background_layer); +} + +static void +desktop_shell_set_background(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output_resource, + struct wl_resource *surface_resource) +{ + struct desktop_shell *shell = wl_resource_get_user_data(resource); + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + struct weston_view *view, *next; + + if (surface->configure) { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "surface role already assigned"); + return; + } + + wl_list_for_each_safe(view, next, &surface->views, surface_link) + weston_view_destroy(view); + view = weston_view_create(surface); + + surface->configure = background_configure; + surface->configure_private = shell; + surface->output = wl_resource_get_user_data(output_resource); + view->output = surface->output; + desktop_shell_send_configure(resource, 0, + surface_resource, + surface->output->width, + surface->output->height); +} + +static void +panel_configure(struct weston_surface *es, int32_t sx, int32_t sy) +{ + struct desktop_shell *shell = es->configure_private; + struct weston_view *view; + + view = container_of(es->views.next, struct weston_view, surface_link); + + configure_static_view(view, &shell->panel_layer); +} + +static void +desktop_shell_set_panel(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output_resource, + struct wl_resource *surface_resource) +{ + struct desktop_shell *shell = wl_resource_get_user_data(resource); + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + struct weston_view *view, *next; + + if (surface->configure) { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "surface role already assigned"); + return; + } + + wl_list_for_each_safe(view, next, &surface->views, surface_link) + weston_view_destroy(view); + view = weston_view_create(surface); + + surface->configure = panel_configure; + surface->configure_private = shell; + surface->output = wl_resource_get_user_data(output_resource); + view->output = surface->output; + desktop_shell_send_configure(resource, 0, + surface_resource, + surface->output->width, + surface->output->height); +} + +static void +lock_surface_configure(struct weston_surface *surface, int32_t sx, int32_t sy) +{ + struct desktop_shell *shell = surface->configure_private; + struct weston_view *view; + + view = container_of(surface->views.next, struct weston_view, surface_link); + + if (surface->width == 0) + return; + + center_on_output(view, get_default_output(shell->compositor)); + + if (!weston_surface_is_mapped(surface)) { + wl_list_insert(&shell->lock_layer.view_list, + &view->layer_link); + weston_view_update_transform(view); + shell_fade(shell, FADE_IN); + } +} + +static void +handle_lock_surface_destroy(struct wl_listener *listener, void *data) +{ + struct desktop_shell *shell = + container_of(listener, struct desktop_shell, lock_surface_listener); + + weston_log("lock surface gone\n"); + shell->lock_surface = NULL; +} + +static void +desktop_shell_set_lock_surface(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *surface_resource) +{ + struct desktop_shell *shell = wl_resource_get_user_data(resource); + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + + shell->prepare_event_sent = false; + + if (!shell->locked) + return; + + shell->lock_surface = surface; + + shell->lock_surface_listener.notify = handle_lock_surface_destroy; + wl_signal_add(&surface->destroy_signal, + &shell->lock_surface_listener); + + weston_view_create(surface); + surface->configure = lock_surface_configure; + surface->configure_private = shell; +} + +static void +resume_desktop(struct desktop_shell *shell) +{ + struct workspace *ws = get_current_workspace(shell); + + terminate_screensaver(shell); + + wl_list_remove(&shell->lock_layer.link); + wl_list_insert(&shell->compositor->cursor_layer.link, + &shell->fullscreen_layer.link); + wl_list_insert(&shell->fullscreen_layer.link, + &shell->panel_layer.link); + if (shell->showing_input_panels) { + wl_list_insert(&shell->panel_layer.link, + &shell->input_panel_layer.link); + wl_list_insert(&shell->input_panel_layer.link, + &ws->layer.link); + } else { + wl_list_insert(&shell->panel_layer.link, &ws->layer.link); + } + + restore_focus_state(shell, get_current_workspace(shell)); + + shell->locked = false; + shell_fade(shell, FADE_IN); + weston_compositor_damage_all(shell->compositor); +} + +static void +desktop_shell_unlock(struct wl_client *client, + struct wl_resource *resource) +{ + struct desktop_shell *shell = wl_resource_get_user_data(resource); + + shell->prepare_event_sent = false; + + if (shell->locked) + resume_desktop(shell); +} + +static void +desktop_shell_set_grab_surface(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *surface_resource) +{ + struct desktop_shell *shell = wl_resource_get_user_data(resource); + + shell->grab_surface = wl_resource_get_user_data(surface_resource); + weston_view_create(shell->grab_surface); +} + +static void +desktop_shell_desktop_ready(struct wl_client *client, + struct wl_resource *resource) +{ + struct desktop_shell *shell = wl_resource_get_user_data(resource); + + shell_fade_startup(shell); +} + +static const struct desktop_shell_interface desktop_shell_implementation = { + desktop_shell_set_background, + desktop_shell_set_panel, + desktop_shell_set_lock_surface, + desktop_shell_unlock, + desktop_shell_set_grab_surface, + desktop_shell_desktop_ready +}; + +static enum shell_surface_type +get_shell_surface_type(struct weston_surface *surface) +{ + struct shell_surface *shsurf; + + shsurf = get_shell_surface(surface); + if (!shsurf) + return SHELL_SURFACE_NONE; + return shsurf->type; +} + +static void +move_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void *data) +{ + struct weston_surface *focus; + struct weston_surface *surface; + struct shell_surface *shsurf; + + if (seat->pointer->focus == NULL) + return; + + focus = seat->pointer->focus->surface; + + surface = weston_surface_get_main_surface(focus); + if (surface == NULL) + return; + + shsurf = get_shell_surface(surface); + if (shsurf == NULL || shsurf->state.fullscreen || + shsurf->state.maximized) + return; + + surface_move(shsurf, (struct weston_seat *) seat); +} + +static void +maximize_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void *data) +{ + struct weston_surface *focus = seat->pointer->focus->surface; + struct weston_surface *surface; + struct shell_surface *shsurf; + + surface = weston_surface_get_main_surface(focus); + if (surface == NULL) + return; + + shsurf = get_shell_surface(surface); + if (shsurf == NULL) + return; + + if (!shell_surface_is_xdg_surface(shsurf)) + return; + + if (shsurf->state.maximized) + xdg_surface_send_request_unset_maximized(shsurf->resource); + else + xdg_surface_send_request_set_maximized(shsurf->resource); +} + +static void +fullscreen_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void *data) +{ + struct weston_surface *focus = seat->pointer->focus->surface; + struct weston_surface *surface; + struct shell_surface *shsurf; + + surface = weston_surface_get_main_surface(focus); + if (surface == NULL) + return; + + shsurf = get_shell_surface(surface); + if (shsurf == NULL) + return; + + if (!shell_surface_is_xdg_surface(shsurf)) + return; + + if (shsurf->state.fullscreen) + xdg_surface_send_request_unset_fullscreen(shsurf->resource); + else + xdg_surface_send_request_set_fullscreen(shsurf->resource); +} + +static void +touch_move_binding(struct weston_seat *seat, uint32_t time, void *data) +{ + struct weston_surface *focus = seat->touch->focus->surface; + struct weston_surface *surface; + struct shell_surface *shsurf; + + surface = weston_surface_get_main_surface(focus); + if (surface == NULL) + return; + + shsurf = get_shell_surface(surface); + if (shsurf == NULL || shsurf->state.fullscreen || + shsurf->state.maximized) + return; + + surface_touch_move(shsurf, (struct weston_seat *) seat); +} + +static void +resize_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void *data) +{ + struct weston_surface *focus; + struct weston_surface *surface; + uint32_t edges = 0; + int32_t x, y; + struct shell_surface *shsurf; + + if (seat->pointer->focus == NULL) + return; + + focus = seat->pointer->focus->surface; + + surface = weston_surface_get_main_surface(focus); + if (surface == NULL) + return; + + shsurf = get_shell_surface(surface); + if (shsurf == NULL || shsurf->state.fullscreen || + shsurf->state.maximized) + return; + + weston_view_from_global(shsurf->view, + wl_fixed_to_int(seat->pointer->grab_x), + wl_fixed_to_int(seat->pointer->grab_y), + &x, &y); + + if (x < shsurf->surface->width / 3) + edges |= WL_SHELL_SURFACE_RESIZE_LEFT; + else if (x < 2 * shsurf->surface->width / 3) + edges |= 0; + else + edges |= WL_SHELL_SURFACE_RESIZE_RIGHT; + + if (y < shsurf->surface->height / 3) + edges |= WL_SHELL_SURFACE_RESIZE_TOP; + else if (y < 2 * shsurf->surface->height / 3) + edges |= 0; + else + edges |= WL_SHELL_SURFACE_RESIZE_BOTTOM; + + surface_resize(shsurf, (struct weston_seat *) seat, edges); +} + +static void +surface_opacity_binding(struct weston_seat *seat, uint32_t time, uint32_t axis, + wl_fixed_t value, void *data) +{ + float step = 0.005; + struct shell_surface *shsurf; + struct weston_surface *focus = seat->pointer->focus->surface; + struct weston_surface *surface; + + /* XXX: broken for windows containing sub-surfaces */ + surface = weston_surface_get_main_surface(focus); + if (surface == NULL) + return; + + shsurf = get_shell_surface(surface); + if (!shsurf) + return; + + shsurf->view->alpha -= wl_fixed_to_double(value) * step; + + if (shsurf->view->alpha > 1.0) + shsurf->view->alpha = 1.0; + if (shsurf->view->alpha < step) + shsurf->view->alpha = step; + + weston_view_geometry_dirty(shsurf->view); + weston_surface_damage(surface); +} + +static void +do_zoom(struct weston_seat *seat, uint32_t time, uint32_t key, uint32_t axis, + wl_fixed_t value) +{ + struct weston_seat *ws = (struct weston_seat *) seat; + struct weston_compositor *compositor = ws->compositor; + struct weston_output *output; + float increment; + + wl_list_for_each(output, &compositor->output_list, link) { + if (pixman_region32_contains_point(&output->region, + wl_fixed_to_double(seat->pointer->x), + wl_fixed_to_double(seat->pointer->y), + NULL)) { + if (key == KEY_PAGEUP) + increment = output->zoom.increment; + else if (key == KEY_PAGEDOWN) + increment = -output->zoom.increment; + else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) + /* For every pixel zoom 20th of a step */ + increment = output->zoom.increment * + -wl_fixed_to_double(value) / 20.0; + else + increment = 0; + + output->zoom.level += increment; + + if (output->zoom.level < 0.0) + output->zoom.level = 0.0; + else if (output->zoom.level > output->zoom.max_level) + output->zoom.level = output->zoom.max_level; + else if (!output->zoom.active) { + weston_output_activate_zoom(output); + } + + output->zoom.spring_z.target = output->zoom.level; + + weston_output_update_zoom(output); + } + } +} + +static void +zoom_axis_binding(struct weston_seat *seat, uint32_t time, uint32_t axis, + wl_fixed_t value, void *data) +{ + do_zoom(seat, time, 0, axis, value); +} + +static void +zoom_key_binding(struct weston_seat *seat, uint32_t time, uint32_t key, + void *data) +{ + do_zoom(seat, time, key, 0, 0); +} + +static void +terminate_binding(struct weston_seat *seat, uint32_t time, uint32_t key, + void *data) +{ + struct weston_compositor *compositor = data; + + wl_display_terminate(compositor->wl_display); +} + +static void +rotate_grab_motion(struct weston_pointer_grab *grab, uint32_t time, + wl_fixed_t x, wl_fixed_t y) +{ + struct rotate_grab *rotate = + container_of(grab, struct rotate_grab, base.grab); + struct weston_pointer *pointer = grab->pointer; + struct shell_surface *shsurf = rotate->base.shsurf; + float cx, cy, dx, dy, cposx, cposy, dposx, dposy, r; + + weston_pointer_move(pointer, x, y); + + if (!shsurf) + return; + + cx = 0.5f * shsurf->surface->width; + cy = 0.5f * shsurf->surface->height; + + dx = wl_fixed_to_double(pointer->x) - rotate->center.x; + dy = wl_fixed_to_double(pointer->y) - rotate->center.y; + r = sqrtf(dx * dx + dy * dy); + + wl_list_remove(&shsurf->rotation.transform.link); + weston_view_geometry_dirty(shsurf->view); + + if (r > 20.0f) { + struct weston_matrix *matrix = + &shsurf->rotation.transform.matrix; + + weston_matrix_init(&rotate->rotation); + weston_matrix_rotate_xy(&rotate->rotation, dx / r, dy / r); + + weston_matrix_init(matrix); + weston_matrix_translate(matrix, -cx, -cy, 0.0f); + weston_matrix_multiply(matrix, &shsurf->rotation.rotation); + weston_matrix_multiply(matrix, &rotate->rotation); + weston_matrix_translate(matrix, cx, cy, 0.0f); + + wl_list_insert( + &shsurf->view->geometry.transformation_list, + &shsurf->rotation.transform.link); + } else { + wl_list_init(&shsurf->rotation.transform.link); + weston_matrix_init(&shsurf->rotation.rotation); + weston_matrix_init(&rotate->rotation); + } + + /* We need to adjust the position of the surface + * in case it was resized in a rotated state before */ + cposx = shsurf->view->geometry.x + cx; + cposy = shsurf->view->geometry.y + cy; + dposx = rotate->center.x - cposx; + dposy = rotate->center.y - cposy; + if (dposx != 0.0f || dposy != 0.0f) { + weston_view_set_position(shsurf->view, + shsurf->view->geometry.x + dposx, + shsurf->view->geometry.y + dposy); + } + + /* Repaint implies weston_surface_update_transform(), which + * lazily applies the damage due to rotation update. + */ + weston_compositor_schedule_repaint(shsurf->surface->compositor); +} + +static void +rotate_grab_button(struct weston_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state_w) +{ + struct rotate_grab *rotate = + container_of(grab, struct rotate_grab, base.grab); + struct weston_pointer *pointer = grab->pointer; + struct shell_surface *shsurf = rotate->base.shsurf; + enum wl_pointer_button_state state = state_w; + + if (pointer->button_count == 0 && + state == WL_POINTER_BUTTON_STATE_RELEASED) { + if (shsurf) + weston_matrix_multiply(&shsurf->rotation.rotation, + &rotate->rotation); + shell_grab_end(&rotate->base); + free(rotate); + } +} + +static void +rotate_grab_cancel(struct weston_pointer_grab *grab) +{ + struct rotate_grab *rotate = + container_of(grab, struct rotate_grab, base.grab); + + shell_grab_end(&rotate->base); + free(rotate); +} + +static const struct weston_pointer_grab_interface rotate_grab_interface = { + noop_grab_focus, + rotate_grab_motion, + rotate_grab_button, + rotate_grab_cancel, +}; + +static void +surface_rotate(struct shell_surface *surface, struct weston_seat *seat) +{ + struct rotate_grab *rotate; + float dx, dy; + float r; + + rotate = malloc(sizeof *rotate); + if (!rotate) + return; + + weston_view_to_global_float(surface->view, + surface->surface->width * 0.5f, + surface->surface->height * 0.5f, + &rotate->center.x, &rotate->center.y); + + dx = wl_fixed_to_double(seat->pointer->x) - rotate->center.x; + dy = wl_fixed_to_double(seat->pointer->y) - rotate->center.y; + r = sqrtf(dx * dx + dy * dy); + if (r > 20.0f) { + struct weston_matrix inverse; + + weston_matrix_init(&inverse); + weston_matrix_rotate_xy(&inverse, dx / r, -dy / r); + weston_matrix_multiply(&surface->rotation.rotation, &inverse); + + weston_matrix_init(&rotate->rotation); + weston_matrix_rotate_xy(&rotate->rotation, dx / r, dy / r); + } else { + weston_matrix_init(&surface->rotation.rotation); + weston_matrix_init(&rotate->rotation); + } + + shell_grab_start(&rotate->base, &rotate_grab_interface, surface, + seat->pointer, DESKTOP_SHELL_CURSOR_ARROW); +} + +static void +rotate_binding(struct weston_seat *seat, uint32_t time, uint32_t button, + void *data) +{ + struct weston_surface *focus; + struct weston_surface *base_surface; + struct shell_surface *surface; + + if (seat->pointer->focus == NULL) + return; + + focus = seat->pointer->focus->surface; + + base_surface = weston_surface_get_main_surface(focus); + if (base_surface == NULL) + return; + + surface = get_shell_surface(base_surface); + if (surface == NULL || surface->state.fullscreen || + surface->state.maximized) + return; + + surface_rotate(surface, seat); +} + +/* Move all fullscreen layers down to the current workspace in a non-reversible + * manner. This should be used when implementing shell-wide overlays, such as + * the alt-tab switcher, which need to de-promote fullscreen layers. */ +void +lower_fullscreen_layer(struct desktop_shell *shell) +{ + struct workspace *ws; + struct weston_view *view, *prev; + + ws = get_current_workspace(shell); + wl_list_for_each_reverse_safe(view, prev, + &shell->fullscreen_layer.view_list, + layer_link) { + wl_list_remove(&view->layer_link); + wl_list_insert(&ws->layer.view_list, &view->layer_link); + weston_view_damage_below(view); + weston_surface_damage(view->surface); + } +} + +void +activate(struct desktop_shell *shell, struct weston_surface *es, + struct weston_seat *seat) +{ + struct weston_surface *main_surface; + struct focus_state *state; + struct workspace *ws; + struct weston_surface *old_es; + struct shell_surface *shsurf; + + main_surface = weston_surface_get_main_surface(es); + + weston_surface_activate(es, seat); + + state = ensure_focus_state(shell, seat); + if (state == NULL) + return; + + old_es = state->keyboard_focus; + state->keyboard_focus = es; + wl_list_remove(&state->surface_destroy_listener.link); + wl_signal_add(&es->destroy_signal, &state->surface_destroy_listener); + + shsurf = get_shell_surface(main_surface); + if (shsurf->state.fullscreen) + shell_configure_fullscreen(shsurf); + else + restore_all_output_modes(shell->compositor); + + if (shell->focus_animation_type != ANIMATION_NONE) { + ws = get_current_workspace(shell); + animate_focus_change(shell, ws, get_default_view(old_es), get_default_view(es)); + } + + /* Update the surface’s layer. This brings it to the top of the stacking + * order as appropriate. */ + shell_surface_update_layer(get_shell_surface(main_surface)); +} + +/* no-op func for checking black surface */ +static void +black_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy) +{ +} + +static bool +is_black_surface (struct weston_surface *es, struct weston_surface **fs_surface) +{ + if (es->configure == black_surface_configure) { + if (fs_surface) + *fs_surface = (struct weston_surface *)es->configure_private; + return true; + } + return false; +} + +static void +activate_binding(struct weston_seat *seat, + struct desktop_shell *shell, + struct weston_surface *focus) +{ + struct weston_surface *main_surface; + + if (!focus) + return; + + if (is_black_surface(focus, &main_surface)) + focus = main_surface; + + main_surface = weston_surface_get_main_surface(focus); + if (get_shell_surface_type(main_surface) == SHELL_SURFACE_NONE) + return; + + activate(shell, focus, seat); +} + +static void +click_to_activate_binding(struct weston_seat *seat, uint32_t time, uint32_t button, + void *data) +{ + if (seat->pointer->grab != &seat->pointer->default_grab) + return; + if (seat->pointer->focus == NULL) + return; + + activate_binding(seat, data, seat->pointer->focus->surface); +} + +static void +touch_to_activate_binding(struct weston_seat *seat, uint32_t time, void *data) +{ + if (seat->touch->grab != &seat->touch->default_grab) + return; + if (seat->touch->focus == NULL) + return; + + activate_binding(seat, data, seat->touch->focus->surface); +} + +static void +lock(struct desktop_shell *shell) +{ + struct workspace *ws = get_current_workspace(shell); + + if (shell->locked) { + weston_compositor_sleep(shell->compositor); + return; + } + + shell->locked = true; + + /* Hide all surfaces by removing the fullscreen, panel and + * toplevel layers. This way nothing else can show or receive + * input events while we are locked. */ + + wl_list_remove(&shell->panel_layer.link); + wl_list_remove(&shell->fullscreen_layer.link); + if (shell->showing_input_panels) + wl_list_remove(&shell->input_panel_layer.link); + wl_list_remove(&ws->layer.link); + wl_list_insert(&shell->compositor->cursor_layer.link, + &shell->lock_layer.link); + + launch_screensaver(shell); + + /* TODO: disable bindings that should not work while locked. */ + + /* All this must be undone in resume_desktop(). */ +} + +static void +unlock(struct desktop_shell *shell) +{ + if (!shell->locked || shell->lock_surface) { + shell_fade(shell, FADE_IN); + return; + } + + /* If desktop-shell client has gone away, unlock immediately. */ + if (!shell->child.desktop_shell) { + resume_desktop(shell); + return; + } + + if (shell->prepare_event_sent) + return; + + desktop_shell_send_prepare_lock_surface(shell->child.desktop_shell); + shell->prepare_event_sent = true; +} + +static void +shell_fade_done(struct weston_view_animation *animation, void *data) +{ + struct desktop_shell *shell = data; + + shell->fade.animation = NULL; + + switch (shell->fade.type) { + case FADE_IN: + weston_surface_destroy(shell->fade.view->surface); + shell->fade.view = NULL; + break; + case FADE_OUT: + lock(shell); + break; + default: + break; + } +} + +static struct weston_view * +shell_fade_create_surface(struct desktop_shell *shell) +{ + struct weston_compositor *compositor = shell->compositor; + struct weston_surface *surface; + struct weston_view *view; + + surface = weston_surface_create(compositor); + if (!surface) + return NULL; + + view = weston_view_create(surface); + if (!view) { + weston_surface_destroy(surface); + return NULL; + } + + weston_surface_set_size(surface, 8192, 8192); + weston_view_set_position(view, 0, 0); + weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0); + wl_list_insert(&compositor->fade_layer.view_list, + &view->layer_link); + pixman_region32_init(&surface->input); + + return view; +} + +static void +shell_fade(struct desktop_shell *shell, enum fade_type type) +{ + float tint; + + switch (type) { + case FADE_IN: + tint = 0.0; + break; + case FADE_OUT: + tint = 1.0; + break; + default: + weston_log("shell: invalid fade type\n"); + return; + } + + shell->fade.type = type; + + if (shell->fade.view == NULL) { + shell->fade.view = shell_fade_create_surface(shell); + if (!shell->fade.view) + return; + + shell->fade.view->alpha = 1.0 - tint; + weston_view_update_transform(shell->fade.view); + } + + if (shell->fade.animation) + weston_fade_update(shell->fade.animation, tint); + else + shell->fade.animation = + weston_fade_run(shell->fade.view, + 1.0 - tint, tint, 300.0, + shell_fade_done, shell); +} + +static void +do_shell_fade_startup(void *data) +{ + struct desktop_shell *shell = data; + + if (shell->startup_animation_type == ANIMATION_FADE) + shell_fade(shell, FADE_IN); + else if (shell->startup_animation_type == ANIMATION_NONE) { + weston_surface_destroy(shell->fade.view->surface); + shell->fade.view = NULL; + } +} + +static void +shell_fade_startup(struct desktop_shell *shell) +{ + struct wl_event_loop *loop; + + if (!shell->fade.startup_timer) + return; + + wl_event_source_remove(shell->fade.startup_timer); + shell->fade.startup_timer = NULL; + + loop = wl_display_get_event_loop(shell->compositor->wl_display); + wl_event_loop_add_idle(loop, do_shell_fade_startup, shell); +} + +static int +fade_startup_timeout(void *data) +{ + struct desktop_shell *shell = data; + + shell_fade_startup(shell); + return 0; +} + +static void +shell_fade_init(struct desktop_shell *shell) +{ + /* Make compositor output all black, and wait for the desktop-shell + * client to signal it is ready, then fade in. The timer triggers a + * fade-in, in case the desktop-shell client takes too long. + */ + + struct wl_event_loop *loop; + + if (shell->fade.view != NULL) { + weston_log("%s: warning: fade surface already exists\n", + __func__); + return; + } + + shell->fade.view = shell_fade_create_surface(shell); + if (!shell->fade.view) + return; + + weston_view_update_transform(shell->fade.view); + weston_surface_damage(shell->fade.view->surface); + + loop = wl_display_get_event_loop(shell->compositor->wl_display); + shell->fade.startup_timer = + wl_event_loop_add_timer(loop, fade_startup_timeout, shell); + wl_event_source_timer_update(shell->fade.startup_timer, 15000); +} + +static void +idle_handler(struct wl_listener *listener, void *data) +{ + struct desktop_shell *shell = + container_of(listener, struct desktop_shell, idle_listener); + + shell_fade(shell, FADE_OUT); + /* lock() is called from shell_fade_done() */ +} + +static void +wake_handler(struct wl_listener *listener, void *data) +{ + struct desktop_shell *shell = + container_of(listener, struct desktop_shell, wake_listener); + + unlock(shell); +} + +static void +center_on_output(struct weston_view *view, struct weston_output *output) +{ + int32_t surf_x, surf_y, width, height; + float x, y; + + surface_subsurfaces_boundingbox(view->surface, &surf_x, &surf_y, &width, &height); + + x = output->x + (output->width - width) / 2 - surf_x / 2; + y = output->y + (output->height - height) / 2 - surf_y / 2; + + weston_view_set_position(view, x, y); +} + +static void +weston_view_set_initial_position(struct weston_view *view, + struct desktop_shell *shell) +{ + struct weston_compositor *compositor = shell->compositor; + int ix = 0, iy = 0; + int range_x, range_y; + int dx, dy, x, y, panel_height; + struct weston_output *output, *target_output = NULL; + struct weston_seat *seat; + + /* As a heuristic place the new window on the same output as the + * pointer. Falling back to the output containing 0, 0. + * + * TODO: Do something clever for touch too? + */ + wl_list_for_each(seat, &compositor->seat_list, link) { + if (seat->pointer) { + ix = wl_fixed_to_int(seat->pointer->x); + iy = wl_fixed_to_int(seat->pointer->y); + break; + } + } + + wl_list_for_each(output, &compositor->output_list, link) { + if (pixman_region32_contains_point(&output->region, ix, iy, NULL)) { + target_output = output; + break; + } + } + + if (!target_output) { + weston_view_set_position(view, 10 + random() % 400, + 10 + random() % 400); + return; + } + + /* Valid range within output where the surface will still be onscreen. + * If this is negative it means that the surface is bigger than + * output. + */ + panel_height = get_output_panel_height(shell, target_output); + range_x = target_output->width - view->surface->width; + range_y = (target_output->height - panel_height) - + view->surface->height; + + if (range_x > 0) + dx = random() % range_x; + else + dx = 0; + + if (range_y > 0) + dy = panel_height + random() % range_y; + else + dy = panel_height; + + x = target_output->x + dx; + y = target_output->y + dy; + + weston_view_set_position(view, x, y); +} + +static void +map(struct desktop_shell *shell, struct shell_surface *shsurf, + int32_t sx, int32_t sy) +{ + struct weston_compositor *compositor = shell->compositor; + struct weston_seat *seat; + int panel_height = 0; + int32_t surf_x, surf_y; + + /* initial positioning, see also configure() */ + switch (shsurf->type) { + case SHELL_SURFACE_TOPLEVEL: + if (shsurf->state.fullscreen) { + center_on_output(shsurf->view, shsurf->fullscreen_output); + shell_map_fullscreen(shsurf); + } else if (shsurf->state.maximized) { + /* use surface configure to set the geometry */ + panel_height = get_output_panel_height(shell, shsurf->output); + surface_subsurfaces_boundingbox(shsurf->surface, + &surf_x, &surf_y, NULL, NULL); + weston_view_set_position(shsurf->view, + shsurf->output->x - surf_x, + shsurf->output->y + + panel_height - surf_y); + } else if (!shsurf->state.relative) { + weston_view_set_initial_position(shsurf->view, shell); + } + break; + case SHELL_SURFACE_POPUP: + shell_map_popup(shsurf); + break; + case SHELL_SURFACE_NONE: + weston_view_set_position(shsurf->view, + shsurf->view->geometry.x + sx, + shsurf->view->geometry.y + sy); + break; + case SHELL_SURFACE_XWAYLAND: + default: + ; + } + + /* Surface stacking order, see also activate(). */ + shell_surface_update_layer(shsurf); + + if (shsurf->type != SHELL_SURFACE_NONE) { + weston_view_update_transform(shsurf->view); + if (shsurf->state.maximized) { + shsurf->surface->output = shsurf->output; + shsurf->view->output = shsurf->output; + } + } + + if ((shsurf->type == SHELL_SURFACE_XWAYLAND || shsurf->state.relative) && + shsurf->transient.flags == WL_SHELL_SURFACE_TRANSIENT_INACTIVE) { + } + + switch (shsurf->type) { + /* XXX: xwayland's using the same fields for transient type */ + case SHELL_SURFACE_XWAYLAND: + if (shsurf->transient.flags == + WL_SHELL_SURFACE_TRANSIENT_INACTIVE) + break; + case SHELL_SURFACE_TOPLEVEL: + if (shsurf->state.relative && + shsurf->transient.flags == WL_SHELL_SURFACE_TRANSIENT_INACTIVE) + break; + if (shell->locked) + break; + wl_list_for_each(seat, &compositor->seat_list, link) + activate(shell, shsurf->surface, seat); + break; + case SHELL_SURFACE_POPUP: + case SHELL_SURFACE_NONE: + default: + break; + } + + if (shsurf->type == SHELL_SURFACE_TOPLEVEL && + !shsurf->state.maximized && !shsurf->state.fullscreen) + { + switch (shell->win_animation_type) { + case ANIMATION_FADE: + weston_fade_run(shsurf->view, 0.0, 1.0, 300.0, NULL, NULL); + break; + case ANIMATION_ZOOM: + weston_zoom_run(shsurf->view, 0.5, 1.0, NULL, NULL); + break; + case ANIMATION_NONE: + default: + break; + } + } +} + +static void +configure(struct desktop_shell *shell, struct weston_surface *surface, + float x, float y) +{ + struct shell_surface *shsurf; + struct weston_view *view; + int32_t mx, my, surf_x, surf_y; + + shsurf = get_shell_surface(surface); + + if (shsurf->state.fullscreen) + shell_configure_fullscreen(shsurf); + else if (shsurf->state.maximized) { + /* setting x, y and using configure to change that geometry */ + surface_subsurfaces_boundingbox(shsurf->surface, &surf_x, &surf_y, + NULL, NULL); + mx = shsurf->output->x - surf_x; + my = shsurf->output->y + + get_output_panel_height(shell,shsurf->output) - surf_y; + weston_view_set_position(shsurf->view, mx, my); + } else { + weston_view_set_position(shsurf->view, x, y); + } + + /* XXX: would a fullscreen surface need the same handling? */ + if (surface->output) { + wl_list_for_each(view, &surface->views, surface_link) + weston_view_update_transform(view); + + if (shsurf->state.maximized) + surface->output = shsurf->output; + } +} + +static void +shell_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy) +{ + struct shell_surface *shsurf = get_shell_surface(es); + struct desktop_shell *shell = shsurf->shell; + + int type_changed = 0; + + if (!weston_surface_is_mapped(es) && + !wl_list_empty(&shsurf->popup.grab_link)) { + remove_popup_grab(shsurf); + } + + if (es->width == 0) + return; + + if (shsurf->state_changed) { + set_surface_type(shsurf); + type_changed = 1; + } + + if (!weston_surface_is_mapped(es)) { + map(shell, shsurf, sx, sy); + } else if (type_changed || sx != 0 || sy != 0 || + shsurf->last_width != es->width || + shsurf->last_height != es->height) { + shsurf->last_width = es->width; + shsurf->last_height = es->height; + float from_x, from_y; + float to_x, to_y; + + weston_view_to_global_float(shsurf->view, 0, 0, &from_x, &from_y); + weston_view_to_global_float(shsurf->view, sx, sy, &to_x, &to_y); + configure(shell, es, + shsurf->view->geometry.x + to_x - from_x, + shsurf->view->geometry.y + to_y - from_y); + } +} + +static void +shell_surface_output_destroyed(struct weston_surface *es) +{ + struct shell_surface *shsurf = get_shell_surface(es); + + shsurf->saved_position_valid = false; + shsurf->next_state.maximized = false; + shsurf->next_state.fullscreen = false; + shsurf->state_changed = true; +} + +static void launch_desktop_shell_process(void *data); + +static void +desktop_shell_sigchld(struct weston_process *process, int status) +{ + uint32_t time; + struct desktop_shell *shell = + container_of(process, struct desktop_shell, child.process); + + shell->child.process.pid = 0; + shell->child.client = NULL; /* already destroyed by wayland */ + + /* if desktop-shell dies more than 5 times in 30 seconds, give up */ + time = weston_compositor_get_time(); + if (time - shell->child.deathstamp > 30000) { + shell->child.deathstamp = time; + shell->child.deathcount = 0; + } + + shell->child.deathcount++; + if (shell->child.deathcount > 5) { + weston_log("%s died, giving up.\n", shell->client); + return; + } + + weston_log("%s died, respawning...\n", shell->client); + launch_desktop_shell_process(shell); + shell_fade_startup(shell); +} + +static void +desktop_shell_client_destroy(struct wl_listener *listener, void *data) +{ + struct desktop_shell *shell; + + shell = container_of(listener, struct desktop_shell, + child.client_destroy_listener); + + shell->child.client = NULL; +} + +static void +launch_desktop_shell_process(void *data) +{ + struct desktop_shell *shell = data; + + shell->child.client = weston_client_launch(shell->compositor, + &shell->child.process, + shell->client, + desktop_shell_sigchld); + + if (!shell->child.client) + weston_log("not able to start %s\n", shell->client); + + shell->child.client_destroy_listener.notify = + desktop_shell_client_destroy; + wl_client_add_destroy_listener(shell->child.client, + &shell->child.client_destroy_listener); +} + +static void +bind_shell(struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct desktop_shell *shell = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, &wl_shell_interface, 1, id); + if (resource) + wl_resource_set_implementation(resource, &shell_implementation, + shell, NULL); +} + +static void +bind_xdg_shell(struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct desktop_shell *shell = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, &xdg_shell_interface, 1, id); + if (resource) + wl_resource_set_dispatcher(resource, + xdg_shell_unversioned_dispatch, + NULL, shell, NULL); +} + +static void +unbind_desktop_shell(struct wl_resource *resource) +{ + struct desktop_shell *shell = wl_resource_get_user_data(resource); + + if (shell->locked) + resume_desktop(shell); + + shell->child.desktop_shell = NULL; + shell->prepare_event_sent = false; +} + +static void +bind_desktop_shell(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct desktop_shell *shell = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, &desktop_shell_interface, + MIN(version, 2), id); + + if (client == shell->child.client) { + wl_resource_set_implementation(resource, + &desktop_shell_implementation, + shell, unbind_desktop_shell); + shell->child.desktop_shell = resource; + + if (version < 2) + shell_fade_startup(shell); + + return; + } + + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "permission to bind desktop_shell denied"); + wl_resource_destroy(resource); +} + +static void +screensaver_configure(struct weston_surface *surface, int32_t sx, int32_t sy) +{ + struct desktop_shell *shell = surface->configure_private; + struct weston_view *view; + + if (surface->width == 0) + return; + + /* XXX: starting weston-screensaver beforehand does not work */ + if (!shell->locked) + return; + + view = container_of(surface->views.next, struct weston_view, surface_link); + center_on_output(view, surface->output); + + if (wl_list_empty(&view->layer_link)) { + wl_list_insert(shell->lock_layer.view_list.prev, + &view->layer_link); + weston_view_update_transform(view); + wl_event_source_timer_update(shell->screensaver.timer, + shell->screensaver.duration); + shell_fade(shell, FADE_IN); + } +} + +static void +screensaver_set_surface(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *surface_resource, + struct wl_resource *output_resource) +{ + struct desktop_shell *shell = wl_resource_get_user_data(resource); + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + struct weston_output *output = wl_resource_get_user_data(output_resource); + struct weston_view *view, *next; + + /* Make sure we only have one view */ + wl_list_for_each_safe(view, next, &surface->views, surface_link) + weston_view_destroy(view); + weston_view_create(surface); + + surface->configure = screensaver_configure; + surface->configure_private = shell; + surface->output = output; +} + +static const struct screensaver_interface screensaver_implementation = { + screensaver_set_surface +}; + +static void +unbind_screensaver(struct wl_resource *resource) +{ + struct desktop_shell *shell = wl_resource_get_user_data(resource); + + shell->screensaver.binding = NULL; +} + +static void +bind_screensaver(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct desktop_shell *shell = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, &screensaver_interface, 1, id); + + if (shell->screensaver.binding == NULL) { + wl_resource_set_implementation(resource, + &screensaver_implementation, + shell, unbind_screensaver); + shell->screensaver.binding = resource; + return; + } + + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "interface object already bound"); + wl_resource_destroy(resource); +} + +struct switcher { + struct desktop_shell *shell; + struct weston_surface *current; + struct wl_listener listener; + struct weston_keyboard_grab grab; +}; + +static void +switcher_next(struct switcher *switcher) +{ + struct weston_view *view; + struct weston_surface *first = NULL, *prev = NULL, *next = NULL; + struct shell_surface *shsurf; + struct workspace *ws = get_current_workspace(switcher->shell); + + wl_list_for_each(view, &ws->layer.view_list, layer_link) { + shsurf = get_shell_surface(view->surface); + if (shsurf && + shsurf->type == SHELL_SURFACE_TOPLEVEL && + shsurf->parent == NULL) { + if (first == NULL) + first = view->surface; + if (prev == switcher->current) + next = view->surface; + prev = view->surface; + view->alpha = 0.25; + weston_view_geometry_dirty(view); + weston_surface_damage(view->surface); + } + + if (is_black_surface(view->surface, NULL)) { + view->alpha = 0.25; + weston_view_geometry_dirty(view); + weston_surface_damage(view->surface); + } + } + + if (next == NULL) + next = first; + + if (next == NULL) + return; + + wl_list_remove(&switcher->listener.link); + wl_signal_add(&next->destroy_signal, &switcher->listener); + + switcher->current = next; + wl_list_for_each(view, &next->views, surface_link) + view->alpha = 1.0; + + shsurf = get_shell_surface(switcher->current); + if (shsurf && shsurf->state.fullscreen) + shsurf->fullscreen.black_view->alpha = 1.0; +} + +static void +switcher_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct switcher *switcher = + container_of(listener, struct switcher, listener); + + switcher_next(switcher); +} + +static void +switcher_destroy(struct switcher *switcher) +{ + struct weston_view *view; + struct weston_keyboard *keyboard = switcher->grab.keyboard; + struct workspace *ws = get_current_workspace(switcher->shell); + + wl_list_for_each(view, &ws->layer.view_list, layer_link) { + if (is_focus_view(view)) + continue; + + view->alpha = 1.0; + weston_surface_damage(view->surface); + } + + if (switcher->current) + activate(switcher->shell, switcher->current, + (struct weston_seat *) keyboard->seat); + wl_list_remove(&switcher->listener.link); + weston_keyboard_end_grab(keyboard); + if (keyboard->input_method_resource) + keyboard->grab = &keyboard->input_method_grab; + free(switcher); +} + +static void +switcher_key(struct weston_keyboard_grab *grab, + uint32_t time, uint32_t key, uint32_t state_w) +{ + struct switcher *switcher = container_of(grab, struct switcher, grab); + enum wl_keyboard_key_state state = state_w; + + if (key == KEY_TAB && state == WL_KEYBOARD_KEY_STATE_PRESSED) + switcher_next(switcher); +} + +static void +switcher_modifier(struct weston_keyboard_grab *grab, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ + struct switcher *switcher = container_of(grab, struct switcher, grab); + struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat; + + if ((seat->modifier_state & switcher->shell->binding_modifier) == 0) + switcher_destroy(switcher); +} + +static void +switcher_cancel(struct weston_keyboard_grab *grab) +{ + struct switcher *switcher = container_of(grab, struct switcher, grab); + + switcher_destroy(switcher); +} + +static const struct weston_keyboard_grab_interface switcher_grab = { + switcher_key, + switcher_modifier, + switcher_cancel, +}; + +static void +switcher_binding(struct weston_seat *seat, uint32_t time, uint32_t key, + void *data) +{ + struct desktop_shell *shell = data; + struct switcher *switcher; + + switcher = malloc(sizeof *switcher); + switcher->shell = shell; + switcher->current = NULL; + switcher->listener.notify = switcher_handle_surface_destroy; + wl_list_init(&switcher->listener.link); + + restore_all_output_modes(shell->compositor); + lower_fullscreen_layer(switcher->shell); + switcher->grab.interface = &switcher_grab; + weston_keyboard_start_grab(seat->keyboard, &switcher->grab); + weston_keyboard_set_focus(seat->keyboard, NULL); + switcher_next(switcher); +} + +static void +backlight_binding(struct weston_seat *seat, uint32_t time, uint32_t key, + void *data) +{ + struct weston_compositor *compositor = data; + struct weston_output *output; + long backlight_new = 0; + + /* TODO: we're limiting to simple use cases, where we assume just + * control on the primary display. We'd have to extend later if we + * ever get support for setting backlights on random desktop LCD + * panels though */ + output = get_default_output(compositor); + if (!output) + return; + + if (!output->set_backlight) + return; + + if (key == KEY_F9 || key == KEY_BRIGHTNESSDOWN) + backlight_new = output->backlight_current - 25; + else if (key == KEY_F10 || key == KEY_BRIGHTNESSUP) + backlight_new = output->backlight_current + 25; + + if (backlight_new < 5) + backlight_new = 5; + if (backlight_new > 255) + backlight_new = 255; + + output->backlight_current = backlight_new; + output->set_backlight(output, output->backlight_current); +} + +struct debug_binding_grab { + struct weston_keyboard_grab grab; + struct weston_seat *seat; + uint32_t key[2]; + int key_released[2]; +}; + +static void +debug_binding_key(struct weston_keyboard_grab *grab, uint32_t time, + uint32_t key, uint32_t state) +{ + struct debug_binding_grab *db = (struct debug_binding_grab *) grab; + struct weston_compositor *ec = db->seat->compositor; + struct wl_display *display = ec->wl_display; + struct wl_resource *resource; + uint32_t serial; + int send = 0, terminate = 0; + int check_binding = 1; + int i; + struct wl_list *resource_list; + + if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { + /* Do not run bindings on key releases */ + check_binding = 0; + + for (i = 0; i < 2; i++) + if (key == db->key[i]) + db->key_released[i] = 1; + + if (db->key_released[0] && db->key_released[1]) { + /* All key releases been swalled so end the grab */ + terminate = 1; + } else if (key != db->key[0] && key != db->key[1]) { + /* Should not swallow release of other keys */ + send = 1; + } + } else if (key == db->key[0] && !db->key_released[0]) { + /* Do not check bindings for the first press of the binding + * key. This allows it to be used as a debug shortcut. + * We still need to swallow this event. */ + check_binding = 0; + } else if (db->key[1]) { + /* If we already ran a binding don't process another one since + * we can't keep track of all the binding keys that were + * pressed in order to swallow the release events. */ + send = 1; + check_binding = 0; + } + + if (check_binding) { + if (weston_compositor_run_debug_binding(ec, db->seat, time, + key, state)) { + /* We ran a binding so swallow the press and keep the + * grab to swallow the released too. */ + send = 0; + terminate = 0; + db->key[1] = key; + } else { + /* Terminate the grab since the key pressed is not a + * debug binding key. */ + send = 1; + terminate = 1; + } + } + + if (send) { + serial = wl_display_next_serial(display); + resource_list = &grab->keyboard->focus_resource_list; + wl_resource_for_each(resource, resource_list) { + wl_keyboard_send_key(resource, serial, time, key, state); + } + } + + if (terminate) { + weston_keyboard_end_grab(grab->keyboard); + if (grab->keyboard->input_method_resource) + grab->keyboard->grab = &grab->keyboard->input_method_grab; + free(db); + } +} + +static void +debug_binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ + struct wl_resource *resource; + struct wl_list *resource_list; + + resource_list = &grab->keyboard->focus_resource_list; + + wl_resource_for_each(resource, resource_list) { + wl_keyboard_send_modifiers(resource, serial, mods_depressed, + mods_latched, mods_locked, group); + } +} + +static void +debug_binding_cancel(struct weston_keyboard_grab *grab) +{ + struct debug_binding_grab *db = (struct debug_binding_grab *) grab; + + weston_keyboard_end_grab(grab->keyboard); + free(db); +} + +struct weston_keyboard_grab_interface debug_binding_keyboard_grab = { + debug_binding_key, + debug_binding_modifiers, + debug_binding_cancel, +}; + +static void +debug_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data) +{ + struct debug_binding_grab *grab; + + grab = calloc(1, sizeof *grab); + if (!grab) + return; + + grab->seat = (struct weston_seat *) seat; + grab->key[0] = key; + grab->grab.interface = &debug_binding_keyboard_grab; + weston_keyboard_start_grab(seat->keyboard, &grab->grab); +} + +static void +force_kill_binding(struct weston_seat *seat, uint32_t time, uint32_t key, + void *data) +{ + struct weston_surface *focus_surface; + struct wl_client *client; + struct desktop_shell *shell = data; + struct weston_compositor *compositor = shell->compositor; + pid_t pid; + + focus_surface = seat->keyboard->focus; + if (!focus_surface) + return; + + wl_signal_emit(&compositor->kill_signal, focus_surface); + + client = wl_resource_get_client(focus_surface->resource); + wl_client_get_credentials(client, &pid, NULL, NULL); + + /* Skip clients that we launched ourselves (the credentials of + * the socketpair is ours) */ + if (pid == getpid()) + return; + + kill(pid, SIGKILL); +} + +static void +workspace_up_binding(struct weston_seat *seat, uint32_t time, + uint32_t key, void *data) +{ + struct desktop_shell *shell = data; + unsigned int new_index = shell->workspaces.current; + + if (shell->locked) + return; + if (new_index != 0) + new_index--; + + change_workspace(shell, new_index); +} + +static void +workspace_down_binding(struct weston_seat *seat, uint32_t time, + uint32_t key, void *data) +{ + struct desktop_shell *shell = data; + unsigned int new_index = shell->workspaces.current; + + if (shell->locked) + return; + if (new_index < shell->workspaces.num - 1) + new_index++; + + change_workspace(shell, new_index); +} + +static void +workspace_f_binding(struct weston_seat *seat, uint32_t time, + uint32_t key, void *data) +{ + struct desktop_shell *shell = data; + unsigned int new_index; + + if (shell->locked) + return; + new_index = key - KEY_F1; + if (new_index >= shell->workspaces.num) + new_index = shell->workspaces.num - 1; + + change_workspace(shell, new_index); +} + +static void +workspace_move_surface_up_binding(struct weston_seat *seat, uint32_t time, + uint32_t key, void *data) +{ + struct desktop_shell *shell = data; + unsigned int new_index = shell->workspaces.current; + + if (shell->locked) + return; + + if (new_index != 0) + new_index--; + + take_surface_to_workspace_by_seat(shell, seat, new_index); +} + +static void +workspace_move_surface_down_binding(struct weston_seat *seat, uint32_t time, + uint32_t key, void *data) +{ + struct desktop_shell *shell = data; + unsigned int new_index = shell->workspaces.current; + + if (shell->locked) + return; + + if (new_index < shell->workspaces.num - 1) + new_index++; + + take_surface_to_workspace_by_seat(shell, seat, new_index); +} + +static void +handle_output_destroy(struct wl_listener *listener, void *data) +{ + struct shell_output *output_listener = + container_of(listener, struct shell_output, destroy_listener); + + wl_list_remove(&output_listener->destroy_listener.link); + wl_list_remove(&output_listener->link); + free(output_listener); +} + +static void +create_shell_output(struct desktop_shell *shell, + struct weston_output *output) +{ + struct shell_output *shell_output; + + shell_output = zalloc(sizeof *shell_output); + if (shell_output == NULL) + return; + + shell_output->output = output; + shell_output->shell = shell; + shell_output->destroy_listener.notify = handle_output_destroy; + wl_signal_add(&output->destroy_signal, + &shell_output->destroy_listener); + wl_list_insert(shell->output_list.prev, &shell_output->link); +} + +static void +handle_output_create(struct wl_listener *listener, void *data) +{ + struct desktop_shell *shell = + container_of(listener, struct desktop_shell, output_create_listener); + struct weston_output *output = (struct weston_output *)data; + + create_shell_output(shell, output); +} + +static void +setup_output_destroy_handler(struct weston_compositor *ec, + struct desktop_shell *shell) +{ + struct weston_output *output; + + wl_list_init(&shell->output_list); + wl_list_for_each(output, &ec->output_list, link) + create_shell_output(shell, output); + + shell->output_create_listener.notify = handle_output_create; + wl_signal_add(&ec->output_created_signal, + &shell->output_create_listener); +} + +static void +shell_destroy(struct wl_listener *listener, void *data) +{ + struct desktop_shell *shell = + container_of(listener, struct desktop_shell, destroy_listener); + struct workspace **ws; + struct shell_output *shell_output, *tmp; + + if (shell->child.client) + wl_client_destroy(shell->child.client); + + wl_list_remove(&shell->idle_listener.link); + wl_list_remove(&shell->wake_listener.link); + + input_panel_destroy(shell); + + wl_list_for_each_safe(shell_output, tmp, &shell->output_list, link) { + wl_list_remove(&shell_output->destroy_listener.link); + wl_list_remove(&shell_output->link); + free(shell_output); + } + + wl_list_remove(&shell->output_create_listener.link); + + wl_array_for_each(ws, &shell->workspaces.array) + workspace_destroy(*ws); + wl_array_release(&shell->workspaces.array); + + free(shell->screensaver.path); + free(shell->client); + free(shell); +} + +static void +shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) +{ + uint32_t mod; + int i, num_workspace_bindings; + + /* fixed bindings */ + weston_compositor_add_key_binding(ec, KEY_BACKSPACE, + MODIFIER_CTRL | MODIFIER_ALT, + terminate_binding, ec); + weston_compositor_add_button_binding(ec, BTN_LEFT, 0, + click_to_activate_binding, + shell); + weston_compositor_add_touch_binding(ec, 0, + touch_to_activate_binding, + shell); + weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL, + MODIFIER_SUPER | MODIFIER_ALT, + surface_opacity_binding, NULL); + weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL, + MODIFIER_SUPER, zoom_axis_binding, + NULL); + + /* configurable bindings */ + mod = shell->binding_modifier; + weston_compositor_add_key_binding(ec, KEY_PAGEUP, mod, + zoom_key_binding, NULL); + weston_compositor_add_key_binding(ec, KEY_PAGEDOWN, mod, + zoom_key_binding, NULL); + weston_compositor_add_key_binding(ec, KEY_M, mod, maximize_binding, + NULL); + weston_compositor_add_key_binding(ec, KEY_F, mod, fullscreen_binding, + NULL); + weston_compositor_add_button_binding(ec, BTN_LEFT, mod, move_binding, + shell); + weston_compositor_add_touch_binding(ec, mod, touch_move_binding, shell); + weston_compositor_add_button_binding(ec, BTN_MIDDLE, mod, + resize_binding, shell); + + if (ec->capabilities & WESTON_CAP_ROTATION_ANY) + weston_compositor_add_button_binding(ec, BTN_RIGHT, mod, + rotate_binding, NULL); + + weston_compositor_add_key_binding(ec, KEY_TAB, mod, switcher_binding, + shell); + weston_compositor_add_key_binding(ec, KEY_F9, mod, backlight_binding, + ec); + weston_compositor_add_key_binding(ec, KEY_BRIGHTNESSDOWN, 0, + backlight_binding, ec); + weston_compositor_add_key_binding(ec, KEY_F10, mod, backlight_binding, + ec); + weston_compositor_add_key_binding(ec, KEY_BRIGHTNESSUP, 0, + backlight_binding, ec); + weston_compositor_add_key_binding(ec, KEY_K, mod, + force_kill_binding, shell); + weston_compositor_add_key_binding(ec, KEY_UP, mod, + workspace_up_binding, shell); + weston_compositor_add_key_binding(ec, KEY_DOWN, mod, + workspace_down_binding, shell); + weston_compositor_add_key_binding(ec, KEY_UP, mod | MODIFIER_SHIFT, + workspace_move_surface_up_binding, + shell); + weston_compositor_add_key_binding(ec, KEY_DOWN, mod | MODIFIER_SHIFT, + workspace_move_surface_down_binding, + shell); + + weston_compositor_add_modifier_binding(ec, mod, exposay_binding, shell); + + /* Add bindings for mod+F[1-6] for workspace 1 to 6. */ + if (shell->workspaces.num > 1) { + num_workspace_bindings = shell->workspaces.num; + if (num_workspace_bindings > 6) + num_workspace_bindings = 6; + for (i = 0; i < num_workspace_bindings; i++) + weston_compositor_add_key_binding(ec, KEY_F1 + i, mod, + workspace_f_binding, + shell); + } + + /* Debug bindings */ + weston_compositor_add_key_binding(ec, KEY_SPACE, mod | MODIFIER_SHIFT, + debug_binding, shell); +} + +WL_EXPORT int +module_init(struct weston_compositor *ec, + int *argc, char *argv[]) +{ + struct weston_seat *seat; + struct desktop_shell *shell; + struct workspace **pws; + unsigned int i; + struct wl_event_loop *loop; + + shell = zalloc(sizeof *shell); + if (shell == NULL) + return -1; + + shell->compositor = ec; + + shell->destroy_listener.notify = shell_destroy; + wl_signal_add(&ec->destroy_signal, &shell->destroy_listener); + shell->idle_listener.notify = idle_handler; + wl_signal_add(&ec->idle_signal, &shell->idle_listener); + shell->wake_listener.notify = wake_handler; + wl_signal_add(&ec->wake_signal, &shell->wake_listener); + + ec->ping_handler = ping_handler; + ec->shell_interface.shell = shell; + ec->shell_interface.create_shell_surface = create_shell_surface; + ec->shell_interface.get_primary_view = get_primary_view; + ec->shell_interface.set_toplevel = set_toplevel; + ec->shell_interface.set_transient = set_transient; + ec->shell_interface.set_fullscreen = set_fullscreen; + ec->shell_interface.set_xwayland = set_xwayland; + ec->shell_interface.move = surface_move; + ec->shell_interface.resize = surface_resize; + ec->shell_interface.set_title = set_title; + + weston_layer_init(&shell->fullscreen_layer, &ec->cursor_layer.link); + weston_layer_init(&shell->panel_layer, &shell->fullscreen_layer.link); + weston_layer_init(&shell->background_layer, &shell->panel_layer.link); + weston_layer_init(&shell->lock_layer, NULL); + weston_layer_init(&shell->input_panel_layer, NULL); + + wl_array_init(&shell->workspaces.array); + wl_list_init(&shell->workspaces.client_list); + + if (input_panel_setup(shell) < 0) + return -1; + + shell_configuration(shell); + + shell->exposay.state_cur = EXPOSAY_LAYOUT_INACTIVE; + shell->exposay.state_target = EXPOSAY_TARGET_CANCEL; + + for (i = 0; i < shell->workspaces.num; i++) { + pws = wl_array_add(&shell->workspaces.array, sizeof *pws); + if (pws == NULL) + return -1; + + *pws = workspace_create(); + if (*pws == NULL) + return -1; + } + activate_workspace(shell, 0); + + wl_list_init(&shell->workspaces.anim_sticky_list); + wl_list_init(&shell->workspaces.animation.link); + shell->workspaces.animation.frame = animate_workspace_change_frame; + + if (wl_global_create(ec->wl_display, &wl_shell_interface, 1, + shell, bind_shell) == NULL) + return -1; + + if (wl_global_create(ec->wl_display, &xdg_shell_interface, 1, + shell, bind_xdg_shell) == NULL) + return -1; + + if (wl_global_create(ec->wl_display, + &desktop_shell_interface, 2, + shell, bind_desktop_shell) == NULL) + return -1; + + if (wl_global_create(ec->wl_display, &screensaver_interface, 1, + shell, bind_screensaver) == NULL) + return -1; + + if (wl_global_create(ec->wl_display, &workspace_manager_interface, 1, + shell, bind_workspace_manager) == NULL) + return -1; + + shell->child.deathstamp = weston_compositor_get_time(); + + setup_output_destroy_handler(ec, shell); + + loop = wl_display_get_event_loop(ec->wl_display); + wl_event_loop_add_idle(loop, launch_desktop_shell_process, shell); + + shell->screensaver.timer = + wl_event_loop_add_timer(loop, screensaver_timeout, shell); + + wl_list_for_each(seat, &ec->seat_list, link) + create_pointer_focus_listener(seat); + + shell_add_bindings(ec, shell); + + shell_fade_init(shell); + + return 0; +} diff --git a/desktop-shell/shell.h b/desktop-shell/shell.h new file mode 100644 index 00000000..8ef550f6 --- /dev/null +++ b/desktop-shell/shell.h @@ -0,0 +1,220 @@ +/* + * Copyright © 2010-2012 Intel Corporation + * Copyright © 2011-2012 Collabora, Ltd. + * Copyright © 2013 Raspberry Pi Foundation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "compositor.h" + +enum animation_type { + ANIMATION_NONE, + + ANIMATION_ZOOM, + ANIMATION_FADE, + ANIMATION_DIM_LAYER, +}; + +enum fade_type { + FADE_IN, + FADE_OUT +}; + +enum exposay_target_state { + EXPOSAY_TARGET_OVERVIEW, /* show all windows */ + EXPOSAY_TARGET_CANCEL, /* return to normal, same focus */ + EXPOSAY_TARGET_SWITCH, /* return to normal, switch focus */ +}; + +enum exposay_layout_state { + EXPOSAY_LAYOUT_INACTIVE = 0, /* normal desktop */ + EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE, /* in transition to normal */ + EXPOSAY_LAYOUT_OVERVIEW, /* show all windows */ + EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW, /* in transition to all windows */ +}; + +struct focus_surface { + struct weston_surface *surface; + struct weston_view *view; + struct weston_transform workspace_transform; +}; + +struct workspace { + struct weston_layer layer; + + struct wl_list focus_list; + struct wl_listener seat_destroyed_listener; + + struct focus_surface *fsurf_front; + struct focus_surface *fsurf_back; + struct weston_view_animation *focus_animation; +}; + +struct desktop_shell { + struct weston_compositor *compositor; + + struct wl_listener idle_listener; + struct wl_listener wake_listener; + struct wl_listener destroy_listener; + struct wl_listener show_input_panel_listener; + struct wl_listener hide_input_panel_listener; + struct wl_listener update_input_panel_listener; + + struct weston_layer fullscreen_layer; + struct weston_layer panel_layer; + struct weston_layer background_layer; + struct weston_layer lock_layer; + struct weston_layer input_panel_layer; + + struct wl_listener pointer_focus_listener; + struct weston_surface *grab_surface; + + struct { + struct weston_process process; + struct wl_client *client; + struct wl_resource *desktop_shell; + struct wl_listener client_destroy_listener; + + unsigned deathcount; + uint32_t deathstamp; + } child; + + bool locked; + bool showing_input_panels; + bool prepare_event_sent; + + struct { + struct weston_surface *surface; + pixman_box32_t cursor_rectangle; + } text_input; + + struct weston_surface *lock_surface; + struct wl_listener lock_surface_listener; + + struct { + struct wl_array array; + unsigned int current; + unsigned int num; + + struct wl_list client_list; + + struct weston_animation animation; + struct wl_list anim_sticky_list; + int anim_dir; + uint32_t anim_timestamp; + double anim_current; + struct workspace *anim_from; + struct workspace *anim_to; + } workspaces; + + struct { + char *path; + int duration; + struct wl_resource *binding; + struct weston_process process; + struct wl_event_source *timer; + } screensaver; + + struct { + struct wl_resource *binding; + struct wl_list surfaces; + } input_panel; + + struct { + struct weston_view *view; + struct weston_view_animation *animation; + enum fade_type type; + struct wl_event_source *startup_timer; + } fade; + + struct exposay { + /* XXX: Make these exposay_surfaces. */ + struct weston_view *focus_prev; + struct weston_view *focus_current; + struct weston_view *clicked; + struct workspace *workspace; + struct weston_seat *seat; + struct wl_list surface_list; + + struct weston_keyboard_grab grab_kbd; + struct weston_pointer_grab grab_ptr; + + enum exposay_target_state state_target; + enum exposay_layout_state state_cur; + int in_flight; /* number of animations still running */ + + int num_surfaces; + int grid_size; + int surface_size; + + int hpadding_outer; + int vpadding_outer; + int padding_inner; + + int row_current; + int column_current; + + bool mod_pressed; + bool mod_invalid; + } exposay; + + uint32_t binding_modifier; + enum animation_type win_animation_type; + enum animation_type startup_animation_type; + enum animation_type focus_animation_type; + + struct wl_listener output_create_listener; + struct wl_list output_list; + + char *client; +}; + +void +set_alpha_if_fullscreen(struct shell_surface *shsurf); + +struct weston_output * +get_default_output(struct weston_compositor *compositor); + +struct weston_view * +get_default_view(struct weston_surface *surface); + +struct shell_surface * +get_shell_surface(struct weston_surface *surface); + +struct workspace * +get_current_workspace(struct desktop_shell *shell); + +void +lower_fullscreen_layer(struct desktop_shell *shell); + +void +activate(struct desktop_shell *shell, struct weston_surface *es, + struct weston_seat *seat); + +void +exposay_binding(struct weston_seat *seat, + enum weston_keyboard_modifier modifier, + void *data); +int +input_panel_setup(struct desktop_shell *shell); +void +input_panel_destroy(struct desktop_shell *shell); diff --git a/man/Makefile.am b/man/Makefile.am index e4abd8c4..5fb030a1 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -7,6 +7,7 @@ endif MAN_SUBSTS = \ -e 's|__weston_native_backend__|$(WESTON_NATIVE_BACKEND)|g' \ -e 's|__weston_modules_dir__|$(pkglibdir)|g' \ + -e 's|__weston_shell_client__|$(WESTON_SHELL_CLIENT)|g' \ -e 's|__version__|$(PACKAGE_VERSION)|g' SUFFIXES = .1 .5 .7 .man diff --git a/man/weston.ini.man b/man/weston.ini.man index c5ec3218..6be90bf4 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -104,6 +104,30 @@ directory are: .BR xwayland.so .fi .RE +.TP 7 +.TP 7 +.BI "backend=" headless-backend.so +overrides defaults backend. Available backend modules in the +.IR "__weston_modules_dir__" +directory are: +.PP +.RS 10 +.nf +.BR drm-backend.so +.BR fbdev-backend.so +.BR headless-backend.so +.BR rdp-backend.so +.BR rpi-backend.so +.BR wayland-backend.so +.BR x11-backend.so +.fi +.RE +.BI "gbm-format="format +sets the GBM format used for the framebuffer for the GBM backend. Can be +.B xrgb8888, +.B xrgb2101010, +.B rgb565. +By default, xrgb8888 is used. .RS .PP @@ -115,6 +139,11 @@ different shell plugins. .PP The entries that can appear in this section are: .TP 7 +.BI "client=" file +sets the path for the shell client to run. If not specified +.I __weston_shell_client__ +is launched (string). +.TP 7 .BI "background-image=" file sets the path for the background image file (string). .TP 7 @@ -160,6 +189,12 @@ sets the effect used for opening new windows (string). Can be .B none. By default, the fade animation is used. .TP 7 +.BI "focus-animation=" dim-layer +sets the effect used with the focused and unfocused windows. Can be +.B dim-layer, +.B none. +By default, no animation is used. +.TP 7 .BI "binding-modifier=" ctrl sets the modifier key used for common bindings (string), such as moving surfaces, resizing, rotating, switching, closing and setting the transparency @@ -229,7 +264,8 @@ currently only recognized by the drm and x11 backends. .TP 7 .BI "name=" name sets a name for the output (string). The backend uses the name to -identify the output. All X11 output names start with a letter X. The available +identify the output. All X11 output names start with a letter X. All +Wayland output names start with the letters WL. The available output names for DRM backend are listed in the .B "weston-launch(1)" output. @@ -240,6 +276,7 @@ Examples of usage: .BR "LVDS1 " "DRM backend, Laptop internal panel no.1" .BR "VGA1 " "DRM backend, VGA connector no.1" .BR "X1 " "X11 backend, X window no.1" +.BR "WL1 " "Wayland backend, Wayland window no.1" .fi .RE .RS diff --git a/man/weston.man b/man/weston.man index 39d854be..f2d1b4c7 100644 --- a/man/weston.man +++ b/man/weston.man @@ -148,9 +148,27 @@ Name of the Wayland display to connect to, see also .I WAYLAND_DISPLAY of the environment. .TP +.B \-\-fullscreen +Create a single fullscreen output +.TP +\fB\-\-output\-count\fR=\fIN\fR +Create +.I N +Wayland windows to emulate the same number of outputs. +.TP \fB\-\-width\fR=\fIW\fR, \fB\-\-height\fR=\fIH\fR -Make the desktop size +Make all outputs have a size of .IR W x H " pixels." +.TP +.B \-\-scale\fR=\fIN\fR +Give all outputs a scale factor of +.I N. +.TP +.B \-\-use\-pixman +Use the pixman renderer. By default, weston will try to use EGL and +GLES2 for rendering and will fall back to the pixman-based renderer for +software compositing if EGL cannot be used. Passing this option will force +weston to use the pixman renderer. . .SS X11 backend options: .TP diff --git a/protocol/Makefile.am b/protocol/Makefile.am index 924e48f3..5e331a74 100644 --- a/protocol/Makefile.am +++ b/protocol/Makefile.am @@ -1,11 +1,26 @@ -EXTRA_DIST = \ +protocol_sources = \ desktop-shell.xml \ screenshooter.xml \ - tablet-shell.xml \ xserver.xml \ text.xml \ input-method.xml \ workspaces.xml \ - subsurface.xml \ text-cursor-position.xml \ - wayland-test.xml + wayland-test.xml \ + xdg-shell.xml \ + scaler.xml + +if HAVE_XMLLINT +.PHONY: validate + +.%.xml.valid: %.xml + $(AM_V_GEN)$(XMLLINT) --noout --dtdvalid $(dtddir)/wayland.dtd $^ > $@ + +validate: $(protocol_sources:%.xml=.%.xml.valid) + +all-local: validate + +CLEANFILES= $(protocol_sources:%.xml=.%.xml.valid) +EXTRA_DIST=$(protocol_sources) + +endif diff --git a/protocol/scaler.xml b/protocol/scaler.xml new file mode 100644 index 00000000..dfe44b87 --- /dev/null +++ b/protocol/scaler.xml @@ -0,0 +1,156 @@ + + + + + Copyright © 2013 Collabora, Ltd. + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + The global interface exposing surface cropping and scaling + capabilities is used to instantiate an interface extension for a + wl_surface object. This extended interface will then allow + cropping and scaling the surface contents, effectively + disconnecting the direct relationship between the buffer and the + surface size. + + + + + Informs the server that the client will not be using this + protocol object anymore. This does not affect any other objects, + wl_viewport objects included. + + + + + + + + + + Instantiate an interface extension for the given wl_surface to + crop and scale its content. If the given wl_surface already has + a wl_viewport object associated, the viewport_exists + protocol error is raised. + + + + + + + + + + An additional interface to a wl_surface object, which allows the + client to specify the cropping and scaling of the surface + contents. + + This interface allows to define the source rectangle (src_x, + src_y, src_width, src_height) from where to take the wl_buffer + contents, and scale that to destination size (dst_width, + dst_height). This state is double-buffered, and is applied on the + next wl_surface.commit. + + Before the first set request, the wl_surface still behaves as if + there was no crop and scale state. That is, no scaling is applied, + and the surface size is as defined in wl_surface.attach. + + The crop and scale state causes the surface size to become + dst_width, dst_height. This overrides whatever the attached + wl_buffer size is, unless the wl_buffer is NULL. If the wl_buffer is + NULL, the surface has no content and therefore no size. + + The coordinate transformations from buffer pixel coordinates up to + the surface-local coordinates happen in the following order: + 1. buffer_transform (wl_surface.set_buffer_transform) + 2. buffer_scale (wl_surface.set_buffer_scale) + 3. crop and scale (wl_viewport.set) + This means, that the source rectangle coordinates of crop and scale + are given in the coordinates after the buffer transform and scale, + i.e. in the coordinates that would be the surface-local coordinates + if the crop and scale was not applied. + + If the source rectangle is partially or completely outside of the + wl_buffer, then the surface contents are undefined (not void), and + the surface size is still dst_width, dst_height. + + The x, y arguments of wl_surface.attach are applied as normal to + the surface. They indicate how many pixels to remove from the + surface size from the left and the top. In other words, they are + still in the surface-local coordinate system, just like dst_width + and dst_height are. + + If the wl_surface associated with the wl_viewport is destroyed, + the wl_viewport object becomes inert. + + If the wl_viewport object is destroyed, the crop and scale + state is removed from the wl_surface. The change will be applied + on the next wl_surface.commit. + + + + + The associated wl_surface's crop and scale state is removed. + The change is applied on the next wl_surface.commit. + + + + + + + + + + Set the crop and scale state of the associated wl_surface. See + wl_viewport for the description, and relation to the wl_buffer + size. + + The bad_value protocol error is raised if src_width or + src_height is negative, or if dst_width or dst_height is not + positive. + + The crop and scale state is double-buffered state, and will be + applied on the next wl_surface.commit. + + Arguments dst_x and dst_y do not exist here, use the x and y + arguments to wl_surface.attach. The x, y, dst_width, and dst_height + define the surface-local coordinate system irrespective of the + attached wl_buffer size. + + + + + + + + + + + + diff --git a/protocol/subsurface.xml b/protocol/subsurface.xml deleted file mode 100644 index 9e4a658d..00000000 --- a/protocol/subsurface.xml +++ /dev/null @@ -1,244 +0,0 @@ - - - - - Copyright © 2012-2013 Collabora, Ltd. - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - - The global interface exposing sub-surface compositing capabilities. - A wl_surface, that has sub-surfaces associated, is called the - parent surface. Sub-surfaces can be arbitrarily nested and create - a tree of sub-surfaces. - - The root surface in a tree of sub-surfaces is the main - surface. The main surface cannot be a sub-surface, because - sub-surfaces must always have a parent. - - A main surface with its sub-surfaces forms a (compound) window. - For window management purposes, this set of wl_surface objects is - to be considered as a single window, and it should also behave as - such. - - The aim of sub-surfaces is to offload some of the compositing work - within a window from clients to the compositor. A prime example is - a video player with decorations and video in separate wl_surface - objects. This should allow the compositor to pass YUV video buffer - processing to dedicated overlay hardware when possible. - - - - - Informs the server that the client will not be using this - protocol object anymore. This does not affect any other - objects, wl_subsurface objects included. - - - - - - - - - - Create a sub-surface interface for the given surface, and - associate it with the given parent surface. This turns a - plain wl_surface into a sub-surface. - - The to-be sub-surface must not already have a dedicated - purpose, like any shell surface type, cursor image, drag icon, - or sub-surface. Otherwise a protocol error is raised. - - - - - - - - - - - An additional interface to a wl_surface object, which has been - made a sub-surface. A sub-surface has one parent surface. A - sub-surface's size and position are not limited to that of the parent. - Particularly, a sub-surface is not automatically clipped to its - parent's area. - - A sub-surface becomes mapped, when a non-NULL wl_buffer is applied - and the parent surface is mapped. The order of which one happens - first is irrelevant. A sub-surface is hidden if the parent becomes - hidden, or if a NULL wl_buffer is applied. These rules apply - recursively through the tree of surfaces. - - The behaviour of wl_surface.commit request on a sub-surface - depends on the sub-surface's mode. The possible modes are - synchronized and desynchronized, see methods - wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized - mode caches the wl_surface state to be applied when the parent's - state gets applied, and desynchronized mode applies the pending - wl_surface state directly. A sub-surface is initially in the - synchronized mode. - - Sub-surfaces have also other kind of state, which is managed by - wl_subsurface requests, as opposed to wl_surface requests. This - state includes the sub-surface position relative to the parent - surface (wl_subsurface.set_position), and the stacking order of - the parent and its sub-surfaces (wl_subsurface.place_above and - .place_below). This state is applied when the parent surface's - wl_surface state is applied, regardless of the sub-surface's mode. - As the exception, set_sync and set_desync are effective immediately. - - The main surface can be thought to be always in desynchronized mode, - since it does not have a parent in the sub-surfaces sense. - - Even if a sub-surface is in desynchronized mode, it will behave as - in synchronized mode, if its parent surface behaves as in - synchronized mode. This rule is applied recursively throughout the - tree of surfaces. This means, that one can set a sub-surface into - synchronized mode, and then assume that all its child and grand-child - sub-surfaces are synchronized, too, without explicitly setting them. - - If the wl_surface associated with the wl_subsurface is destroyed, the - wl_subsurface object becomes inert. Note, that destroying either object - takes effect immediately. If you need to synchronize the removal - of a sub-surface to the parent surface update, unmap the sub-surface - first by attaching a NULL wl_buffer, update parent, and then destroy - the sub-surface. - - If the parent wl_surface object is destroyed, the sub-surface is - unmapped. - - - - - The sub-surface interface is removed from the wl_surface object - that was turned into a sub-surface with - wl_subcompositor.get_subsurface request. The wl_surface's association - to the parent is deleted, and the wl_surface loses its role as - a sub-surface. The wl_surface is unmapped. - - - - - - - - - - This schedules a sub-surface position change. - The sub-surface will be moved so, that its origin (top-left - corner pixel) will be at the location x, y of the parent surface - coordinate system. The coordinates are not restricted to the parent - surface area. Negative values are allowed. - - The next wl_surface.commit on the parent surface will reset - the sub-surface's position to the scheduled coordinates. - - The initial position is 0, 0. - - - - - - - - - This sub-surface is taken from the stack, and put back just - above the reference surface, changing the z-order of the sub-surfaces. - The reference surface must be one of the sibling surfaces, or the - parent surface. Using any other surface, including this sub-surface, - will cause a protocol error. - - The z-order is double-buffered state, and will be applied on the - next commit of the parent surface. - See wl_surface.commit and wl_subcompositor.get_subsurface. - - A new sub-surface is initially added as the top-most in the stack - of its siblings and parent. - - - - - - - - The sub-surface is placed just below of the reference surface. - See wl_subsurface.place_above. - - - - - - - - Change the commit behaviour of the sub-surface to synchronized - mode, also described as the parent dependant mode. - - In synchronized mode, wl_surface.commit on a sub-surface will - accumulate the committed state in a cache, but the state will - not be applied and hence will not change the compositor output. - The cached state is applied to the sub-surface immediately after - the parent surface's state is applied. This ensures atomic - updates of the parent and all its synchronized sub-surfaces. - Applying the cached state will invalidate the cache, so further - parent surface commits do not (re-)apply old state. - - See wl_subsurface for the recursive effect of this mode. - - - - - - Change the commit behaviour of the sub-surface to desynchronized - mode, also described as independent or freely running mode. - - In desynchronized mode, wl_surface.commit on a sub-surface will - apply the pending state directly, without caching, as happens - normally with a wl_surface. Calling wl_surface.commit on the - parent surface has no effect on the sub-surface's wl_surface - state. This mode allows a sub-surface to be updated on its own. - - If cached state exists when wl_surface.commit is called in - desynchronized mode, the pending state is added to the cached - state, and applied as whole. This invalidates the cache. - - Note: even if a sub-surface is set to desynchronized, a parent - sub-surface may override it to behave as synchronized. For details, - see wl_subsurface. - - If a surface's parent surface behaves as desynchronized, then - the cached state is applied on set_desync. - - - - - diff --git a/protocol/tablet-shell.xml b/protocol/tablet-shell.xml deleted file mode 100644 index 10f17568..00000000 --- a/protocol/tablet-shell.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/protocol/wayland-test.xml b/protocol/wayland-test.xml index 2993f087..18b66253 100644 --- a/protocol/wayland-test.xml +++ b/protocol/wayland-test.xml @@ -51,5 +51,12 @@ + + + + + + diff --git a/protocol/xdg-shell.xml b/protocol/xdg-shell.xml new file mode 100644 index 00000000..4e5cff8d --- /dev/null +++ b/protocol/xdg-shell.xml @@ -0,0 +1,438 @@ + + + + + Copyright © 2008-2013 Kristian Høgsberg + Copyright © 2013 Rafael Antognolli + Copyright © 2013 Jasper St. Pierre + Copyright © 2010-2013 Intel Corporation + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + This interface is implemented by servers that provide + desktop-style user interfaces. + + It allows clients to associate a xdg_surface with + a basic surface. + + + + + Use this enum to check the protocol version, and it will be updated + automatically. + + + + + + + + Use this request in order to enable use of this interface. + + Understand and agree that one is using an unstable interface, + that will likely change in the future, breaking the API. + + + + + + + Create a shell surface for an existing surface. + + Only one shell or popup surface can be associated with a given + surface. + + + + + + + + Create a popup surface for an existing surface. + + Only one shell or popup surface can be associated with a given + surface. + + + + + + + + + + + + + + + + An interface that may be implemented by a wl_surface, for + implementations that provide a desktop-style user interface. + + It provides requests to treat surfaces like windows, allowing to set + properties like maximized, fullscreen, minimized, and to move and resize + them, and associate metadata like title and app id. + + On the server side the object is automatically destroyed when + the related wl_surface is destroyed. On client side, + xdg_surface.destroy() must be called before destroying + the wl_surface object. + + + + + The xdg_surface interface is removed from the wl_surface object + that was turned into a xdg_surface with + xdg_shell.get_xdg_surface request. The xdg_surface properties, + like maximized and fullscreen, are lost. The wl_surface loses + its role as a xdg_surface. The wl_surface is unmapped. + + + + + + Setting a surface as transient of another means that it is child + of another surface. + + Child surfaces are stacked above their parents, and will be + unmapped if the parent is unmapped too. They should not appear + on task bars and alt+tab. + + + + + + + Set a short title for the surface. + + This string may be used to identify the surface in a task bar, + window list, or other user interface elements provided by the + compositor. + + The string must be encoded in UTF-8. + + + + + + + Set an id for the surface. + + The app id identifies the general class of applications to which + the surface belongs. + + It should be the ID that appears in the new desktop entry + specification, the interface name. + + + + + + + A client must respond to a ping event with a pong request or + the client may be deemed unresponsive. + + + + + + + Ping a client to check if it is receiving events and sending + requests. A client is expected to reply with a pong request. + + + + + + + Start a pointer-driven move of the surface. + + This request must be used in response to a button press event. + The server may ignore move requests depending on the state of + the surface (e.g. fullscreen or maximized). + + + + + + + + These values are used to indicate which edge of a surface + is being dragged in a resize operation. The server may + use this information to adapt its behavior, e.g. choose + an appropriate cursor image. + + + + + + + + + + + + + + + Start a pointer-driven resizing of the surface. + + This request must be used in response to a button press event. + The server may ignore resize requests depending on the state of + the surface (e.g. fullscreen or maximized). + + + + + + + + + The configure event asks the client to resize its surface. + + The size is a hint, in the sense that the client is free to + ignore it if it doesn't resize, pick a smaller size (to + satisfy aspect ratio or resize in steps of NxM pixels). + + The edges parameter provides a hint about how the surface + was resized. The client may use this information to decide + how to adjust its content to the new size (e.g. a scrolling + area might adjust its content position to leave the viewable + content unmoved). Valid edge values are from resize_edge enum. + + The client is free to dismiss all but the last configure + event it received. + + The width and height arguments specify the size of the window + in surface local coordinates. + + + + + + + + + + Set the default output used by this surface when it is first mapped. + + If this value is NULL (default), it's up to the compositor to choose + which display will be used to map this surface. + + When fullscreen or maximized state are set on this surface, and it + wasn't mapped yet, the output set with this method will be used. + Otherwise, the output where the surface is currently mapped will be + used. + + + + + + + Event sent from the compositor to the client requesting that the client + goes to a fullscreen state. It's the client job to call set_fullscreen + and really trigger the fullscreen state. + + + + + + Event sent from the compositor to the client requesting that the client + leaves the fullscreen state. It's the client job to call + unset_fullscreen and really leave the fullscreen state. + + + + + + Set the surface as fullscreen. + + After this request, the compositor should send a configure event + informing the output size. + + This request informs the compositor that the next attached buffer + committed will be in a fullscreen state. The buffer size should be the + same size as the size informed in the configure event, if the client + doesn't want to leave any empty area. + + In other words: the next attached buffer after set_maximized is the new + maximized buffer. And the surface will be positioned at the maximized + position on commit. + + A simple way to synchronize and wait for the correct configure event is + to use a wl_display.sync request right after the set_fullscreen + request. When the sync callback returns, the last configure event + received just before it will be the correct one, and should contain the + right size for the surface to maximize. + + Setting one state won't unset another state. Use + xdg_surface.unset_fullscreen for unsetting it. + + + + + + Unset the surface fullscreen state. + + Same negotiation as set_fullscreen must be used. + + + + + + Event sent from the compositor to the client requesting that the client + goes to a maximized state. It's the client job to call set_maximized + and really trigger the maximized state. + + + + + + Event sent from the compositor to the client requesting that the client + leaves the maximized state. It's the client job to call unset_maximized + and really leave the maximized state. + + + + + + Set the surface as maximized. + + After this request, the compositor will send a configure event + informing the output size minus panel and other MW decorations. + + This request informs the compositor that the next attached buffer + committed will be in a maximized state. The buffer size should be the + same size as the size informed in the configure event, if the client + doesn't want to leave any empty area. + + In other words: the next attached buffer after set_maximized is the new + maximized buffer. And the surface will be positioned at the maximized + position on commit. + + A simple way to synchronize and wait for the correct configure event is + to use a wl_display.sync request right after the set_maximized request. + When the sync callback returns, the last configure event received just + before it will be the correct one, and should contain the right size + for the surface to maximize. + + Setting one state won't unset another state. Use + xdg_surface.unset_maximized for unsetting it. + + + + + + Unset the surface maximized state. + + Same negotiation as set_maximized must be used. + + + + + + Set the surface minimized state. + + Setting one state won't unset another state. + + + + + + The focused_set event is sent when this surface has been + activated. Window decorations should be updated accordingly. + + + + + + The focused_unset event is sent when this surface has been + deactivated, because another surface has been activated. Window + decorations should be updated accordingly. + + + + + + + An interface that may be implemented by a wl_surface, for + implementations that provide a desktop-style popups/menus. A popup + surface is a transient surface with an added pointer grab. + + An existing implicit grab will be changed to owner-events mode, + and the popup grab will continue after the implicit grab ends + (i.e. releasing the mouse button does not cause the popup to be + unmapped). + + The popup grab continues until the window is destroyed or a mouse + button is pressed in any other clients window. A click in any of + the clients surfaces is reported as normal, however, clicks in + other clients surfaces will be discarded and trigger the callback. + + The x and y arguments specify the locations of the upper left + corner of the surface relative to the upper left corner of the + parent surface, in surface local coordinates. + + xdg_popup surfaces are always transient for another surface. + + + + + The xdg_surface interface is removed from the wl_surface object + that was turned into a xdg_surface with + xdg_shell.get_xdg_surface request. The xdg_surface properties, + like maximized and fullscreen, are lost. The wl_surface loses + its role as a xdg_surface. The wl_surface is unmapped. + + + + + + A client must respond to a ping event with a pong request or + the client may be deemed unresponsive. + + + + + + + Ping a client to check if it is receiving events and sending + requests. A client is expected to reply with a pong request. + + + + + + + The popup_done event is sent out when a popup grab is broken, + that is, when the users clicks a surface that doesn't belong + to the client owning the popup surface. + + + + + + diff --git a/shared/Makefile.am b/shared/Makefile.am index 2fcff7bb..31fab5f6 100644 --- a/shared/Makefile.am +++ b/shared/Makefile.am @@ -10,6 +10,7 @@ libshared_la_SOURCES = \ os-compatibility.h libshared_cairo_la_CFLAGS = \ + -DDATADIR='"$(datadir)"' \ $(GCC_CFLAGS) \ $(COMPOSITOR_CFLAGS) \ $(PIXMAN_CFLAGS) \ @@ -29,4 +30,5 @@ libshared_cairo_la_SOURCES = \ image-loader.c \ image-loader.h \ cairo-util.c \ + frame.c \ cairo-util.h diff --git a/shared/cairo-util.c b/shared/cairo-util.c index 4305ba69..39485729 100644 --- a/shared/cairo-util.c +++ b/shared/cairo-util.c @@ -320,12 +320,27 @@ load_cairo_surface(const char *filename) width, height, stride); } +void +theme_set_background_source(struct theme *t, cairo_t *cr, uint32_t flags) +{ + cairo_pattern_t *pattern; + + if (flags & THEME_FRAME_ACTIVE) { + pattern = cairo_pattern_create_linear(16, 16, 16, 112); + cairo_pattern_add_color_stop_rgb(pattern, 0.0, 1.0, 1.0, 1.0); + cairo_pattern_add_color_stop_rgb(pattern, 0.2, 0.8, 0.8, 0.8); + cairo_set_source(cr, pattern); + cairo_pattern_destroy(pattern); + } else { + cairo_set_source_rgba(cr, 0.75, 0.75, 0.75, 1); + } +} + struct theme * theme_create(void) { struct theme *t; cairo_t *cr; - cairo_pattern_t *pattern; t = malloc(sizeof *t); if (t == NULL) @@ -352,12 +367,7 @@ theme_create(void) cr = cairo_create(t->active_frame); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - pattern = cairo_pattern_create_linear(16, 16, 16, 112); - cairo_pattern_add_color_stop_rgb(pattern, 0.0, 1.0, 1.0, 1.0); - cairo_pattern_add_color_stop_rgb(pattern, 0.2, 0.8, 0.8, 0.8); - cairo_set_source(cr, pattern); - cairo_pattern_destroy(pattern); - + theme_set_background_source(t, cr, THEME_FRAME_ACTIVE); rounded_rect(cr, 0, 0, 128, 128, t->frame_radius); cairo_fill(cr); @@ -370,7 +380,7 @@ theme_create(void) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 128, 128); cr = cairo_create(t->inactive_frame); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_set_source_rgba(cr, 0.75, 0.75, 0.75, 1); + theme_set_background_source(t, cr, 0); rounded_rect(cr, 0, 0, 128, 128, t->frame_radius); cairo_fill(cr); @@ -408,7 +418,7 @@ theme_render_frame(struct theme *t, cairo_text_extents_t extents; cairo_font_extents_t font_extents; cairo_surface_t *source; - int x, y, margin; + int x, y, margin, top_margin; cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(cr, 0, 0, 0, 0); @@ -429,40 +439,47 @@ theme_render_frame(struct theme *t, else source = t->inactive_frame; + if (title) + top_margin = t->titlebar_height; + else + top_margin = t->width; + tile_source(cr, source, margin, margin, width - margin * 2, height - margin * 2, - t->width, t->titlebar_height); - - cairo_rectangle (cr, margin + t->width, margin, - width - (margin + t->width) * 2, - t->titlebar_height - t->width); - cairo_clip(cr); - - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_select_font_face(cr, "sans", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_BOLD); - cairo_set_font_size(cr, 14); - cairo_text_extents(cr, title, &extents); - cairo_font_extents (cr, &font_extents); - x = (width - extents.width) / 2; - y = margin + - (t->titlebar_height - - font_extents.ascent - font_extents.descent) / 2 + - font_extents.ascent; - - if (flags & THEME_FRAME_ACTIVE) { - cairo_move_to(cr, x + 1, y + 1); - cairo_set_source_rgb(cr, 1, 1, 1); - cairo_show_text(cr, title); - cairo_move_to(cr, x, y); - cairo_set_source_rgb(cr, 0, 0, 0); - cairo_show_text(cr, title); - } else { - cairo_move_to(cr, x, y); - cairo_set_source_rgb(cr, 0.4, 0.4, 0.4); - cairo_show_text(cr, title); + t->width, top_margin); + + if (title) { + cairo_rectangle (cr, margin + t->width, margin, + width - (margin + t->width) * 2, + t->titlebar_height - t->width); + cairo_clip(cr); + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_select_font_face(cr, "sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size(cr, 14); + cairo_text_extents(cr, title, &extents); + cairo_font_extents (cr, &font_extents); + x = (width - extents.width) / 2; + y = margin + + (t->titlebar_height - + font_extents.ascent - font_extents.descent) / 2 + + font_extents.ascent; + + if (flags & THEME_FRAME_ACTIVE) { + cairo_move_to(cr, x + 1, y + 1); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_show_text(cr, title); + cairo_move_to(cr, x, y); + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_show_text(cr, title); + } else { + cairo_move_to(cr, x, y); + cairo_set_source_rgb(cr, 0.4, 0.4, 0.4); + cairo_show_text(cr, title); + } } } @@ -472,10 +489,15 @@ theme_get_location(struct theme *t, int x, int y, { int vlocation, hlocation, location; const int grip_size = 8; - int margin; + int margin, top_margin; margin = (flags & THEME_FRAME_MAXIMIZED) ? 0 : t->margin; + if (flags & THEME_FRAME_NO_TITLE) + top_margin = t->width; + else + top_margin = t->titlebar_height; + if (x < margin) hlocation = THEME_LOCATION_EXTERIOR; else if (margin <= x && x < margin + grip_size) @@ -502,7 +524,7 @@ theme_get_location(struct theme *t, int x, int y, if (location & THEME_LOCATION_EXTERIOR) location = THEME_LOCATION_EXTERIOR; if (location == THEME_LOCATION_INTERIOR && - y < margin + t->titlebar_height) + y < margin + top_margin) location = THEME_LOCATION_TITLEBAR; else if (location == THEME_LOCATION_INTERIOR) location = THEME_LOCATION_CLIENT_AREA; diff --git a/shared/cairo-util.h b/shared/cairo-util.h index 7b403944..7bcbc296 100644 --- a/shared/cairo-util.h +++ b/shared/cairo-util.h @@ -23,6 +23,7 @@ #ifndef _CAIRO_UTIL_H #define _CAIRO_UTIL_H +#include #include void @@ -59,9 +60,12 @@ theme_destroy(struct theme *t); enum { THEME_FRAME_ACTIVE = 1, - THEME_FRAME_MAXIMIZED, + THEME_FRAME_MAXIMIZED = 2, + THEME_FRAME_NO_TITLE = 4 }; +void +theme_set_background_source(struct theme *t, cairo_t *cr, uint32_t flags); void theme_render_frame(struct theme *t, cairo_t *cr, int width, int height, @@ -86,4 +90,124 @@ enum theme_location { enum theme_location theme_get_location(struct theme *t, int x, int y, int width, int height, int flags); +struct frame; + +enum frame_status { + FRAME_STATUS_NONE = 0, + FRAME_STATUS_REPAINT = 0x1, + FRAME_STATUS_MINIMIZE = 0x2, + FRAME_STATUS_MAXIMIZE = 0x4, + FRAME_STATUS_CLOSE = 0x8, + FRAME_STATUS_MENU = 0x10, + FRAME_STATUS_RESIZE = 0x20, + FRAME_STATUS_MOVE = 0x40, + FRAME_STATUS_ALL = 0x7f +}; + +enum frame_flag { + FRAME_FLAG_ACTIVE = 0x1, + FRAME_FLAG_MAXIMIZED = 0x2 +}; + +enum { + FRAME_BUTTON_NONE = 0, + FRAME_BUTTON_CLOSE = 0x1, + FRAME_BUTTON_MAXIMIZE = 0x2, + FRAME_BUTTON_MINIMIZE = 0x4, + FRAME_BUTTON_ALL = 0x7 +}; + +enum frame_button_state { + FRAME_BUTTON_RELEASED = 0, + FRAME_BUTTON_PRESSED = 1 +}; + +struct frame * +frame_create(struct theme *t, int32_t width, int32_t height, uint32_t buttons, + const char *title); + +void +frame_destroy(struct frame *frame); + +/* May set FRAME_STATUS_REPAINT */ +int +frame_set_title(struct frame *frame, const char *title); + +/* May set FRAME_STATUS_REPAINT */ +void +frame_set_flag(struct frame *frame, enum frame_flag flag); + +/* May set FRAME_STATUS_REPAINT */ +void +frame_unset_flag(struct frame *frame, enum frame_flag flag); + +/* May set FRAME_STATUS_REPAINT */ +void +frame_resize(struct frame *frame, int32_t width, int32_t height); + +/* May set FRAME_STATUS_REPAINT */ +void +frame_resize_inside(struct frame *frame, int32_t width, int32_t height); + +int32_t +frame_width(struct frame *frame); + +int32_t +frame_height(struct frame *frame); + +void +frame_interior(struct frame *frame, int32_t *x, int32_t *y, + int32_t *width, int32_t *height); +void +frame_input_rect(struct frame *frame, int32_t *x, int32_t *y, + int32_t *width, int32_t *height); +void +frame_opaque_rect(struct frame *frame, int32_t *x, int32_t *y, + int32_t *width, int32_t *height); + +uint32_t +frame_status(struct frame *frame); + +void +frame_status_clear(struct frame *frame, enum frame_status status); + +/* May set FRAME_STATUS_REPAINT */ +enum theme_location +frame_pointer_enter(struct frame *frame, void *pointer, int x, int y); + +/* May set FRAME_STATUS_REPAINT */ +enum theme_location +frame_pointer_motion(struct frame *frame, void *pointer, int x, int y); + +/* May set FRAME_STATUS_REPAINT */ +void +frame_pointer_leave(struct frame *frame, void *pointer); + +/* Call to indicate that a button has been pressed/released. The return + * value for a button release will be the same as for the corresponding + * press. This allows you to more easily track grabs. If you want the + * actual location, simply keep the location from the last + * frame_pointer_motion call. + * + * May set: + * FRAME_STATUS_MINIMIZE + * FRAME_STATUS_MAXIMIZE + * FRAME_STATUS_CLOSE + * FRAME_STATUS_MENU + * FRAME_STATUS_RESIZE + * FRAME_STATUS_MOVE + */ +enum theme_location +frame_pointer_button(struct frame *frame, void *pointer, + uint32_t button, enum frame_button_state state); + +void +frame_touch_down(struct frame *frame, void *data, int32_t id, int x, int y); + +void +frame_touch_up(struct frame *frame, void *data, int32_t id); + +void +frame_repaint(struct frame *frame, cairo_t *cr); + #endif diff --git a/shared/frame.c b/shared/frame.c new file mode 100644 index 00000000..a501649f --- /dev/null +++ b/shared/frame.c @@ -0,0 +1,848 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * Copyright © 2012-2013 Collabora, Ltd. + * Copyright © 2013 Jason Ekstrand + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "cairo-util.h" + +enum frame_button_flags { + FRAME_BUTTON_ALIGN_RIGHT = 0x1, + FRAME_BUTTON_DECORATED = 0x2, + FRAME_BUTTON_CLICK_DOWN = 0x4, +}; + +struct frame_button { + struct frame *frame; + struct wl_list link; /* buttons_list */ + + cairo_surface_t *icon; + enum frame_button_flags flags; + int hover_count; + int press_count; + + struct { + int x, y; + int width, height; + } allocation; + + enum frame_status status_effect; +}; + +struct frame_pointer_button { + struct wl_list link; + uint32_t button; + enum theme_location press_location; + struct frame_button *frame_button; +}; + +struct frame_pointer { + struct wl_list link; + void *data; + + int x, y; + + struct frame_button *hover_button; + struct wl_list down_buttons; +}; + +struct frame_touch { + struct wl_list link; + void *data; + + int x, y; + + struct frame_button *button; +}; + +struct frame { + int32_t width, height; + char *title; + uint32_t flags; + struct theme *theme; + + struct { + int32_t x, y; + int32_t width, height; + } interior; + int shadow_margin; + int opaque_margin; + int geometry_dirty; + + uint32_t status; + + struct wl_list buttons; + struct wl_list pointers; + struct wl_list touches; +}; + +static struct frame_button * +frame_button_create(struct frame *frame, const char *icon, + enum frame_status status_effect, + enum frame_button_flags flags) +{ + struct frame_button *button; + + button = calloc(1, sizeof *button); + if (!button) + return NULL; + + button->icon = cairo_image_surface_create_from_png(icon); + if (!button->icon) { + free(button); + return NULL; + } + + button->frame = frame; + button->flags = flags; + button->status_effect = status_effect; + + wl_list_insert(frame->buttons.prev, &button->link); + + return button; +} + +static void +frame_button_destroy(struct frame_button *button) +{ + cairo_surface_destroy(button->icon); + free(button); +} + +static void +frame_button_enter(struct frame_button *button) +{ + if (!button->hover_count) + button->frame->status |= FRAME_STATUS_REPAINT; + button->hover_count++; +} + +static void +frame_button_leave(struct frame_button *button, struct frame_pointer *pointer) +{ + button->hover_count--; + if (!button->hover_count) + button->frame->status |= FRAME_STATUS_REPAINT; +} + +static void +frame_button_press(struct frame_button *button) +{ + if (!button->press_count) + button->frame->status |= FRAME_STATUS_REPAINT; + button->press_count++; + + if (button->flags & FRAME_BUTTON_CLICK_DOWN) + button->frame->status |= button->status_effect; +} + +static void +frame_button_release(struct frame_button *button) +{ + button->press_count--; + if (button->press_count) + return; + + button->frame->status |= FRAME_STATUS_REPAINT; + + if (!(button->flags & FRAME_BUTTON_CLICK_DOWN)) + button->frame->status |= button->status_effect; +} + +static void +frame_button_cancel(struct frame_button *button) +{ + button->press_count--; + if (!button->press_count) + button->frame->status |= FRAME_STATUS_REPAINT; +} + +static void +frame_button_repaint(struct frame_button *button, cairo_t *cr) +{ + int x, y; + + if (!button->allocation.width) + return; + if (!button->allocation.height) + return; + + x = button->allocation.x; + y = button->allocation.y; + + cairo_save(cr); + + if (button->flags & FRAME_BUTTON_DECORATED) { + cairo_set_line_width(cr, 1); + + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_rectangle(cr, x, y, 25, 16); + + cairo_stroke_preserve(cr); + + if (button->press_count) { + cairo_set_source_rgb(cr, 0.7, 0.7, 0.7); + } else if (button->hover_count) { + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + } else { + cairo_set_source_rgb(cr, 0.88, 0.88, 0.88); + } + + cairo_fill (cr); + + x += 4; + } + + cairo_set_source_surface(cr, button->icon, x, y); + cairo_paint(cr); + + cairo_restore(cr); +} + +static struct frame_pointer * +frame_pointer_get(struct frame *frame, void *data) +{ + struct frame_pointer *pointer; + + wl_list_for_each(pointer, &frame->pointers, link) + if (pointer->data == data) + return pointer; + + pointer = calloc(1, sizeof *pointer); + if (!pointer) + return NULL; + + pointer->data = data; + wl_list_init(&pointer->down_buttons); + wl_list_insert(&frame->pointers, &pointer->link); + + return pointer; +} + +static void +frame_pointer_destroy(struct frame_pointer *pointer) +{ + wl_list_remove(&pointer->link); + free(pointer); +} + +static struct frame_touch * +frame_touch_get(struct frame *frame, void *data) +{ + struct frame_touch *touch; + + wl_list_for_each(touch, &frame->touches, link) + if (touch->data == data) + return touch; + + touch = calloc(1, sizeof *touch); + if (!touch) + return NULL; + + touch->data = data; + wl_list_insert(&frame->touches, &touch->link); + + return touch; +} + +static void +frame_touch_destroy(struct frame_touch *touch) +{ + wl_list_remove(&touch->link); + free(touch); +} + +struct frame * +frame_create(struct theme *t, int32_t width, int32_t height, uint32_t buttons, + const char *title) +{ + struct frame *frame; + struct frame_button *button; + + frame = calloc(1, sizeof *frame); + if (!frame) + return NULL; + + frame->width = width; + frame->height = height; + frame->flags = 0; + frame->theme = t; + frame->status = FRAME_STATUS_REPAINT; + frame->geometry_dirty = 1; + + if (title) { + frame->title = strdup(title); + if (!frame->title) + goto free_frame; + } + + wl_list_init(&frame->buttons); + wl_list_init(&frame->pointers); + wl_list_init(&frame->touches); + + if (title) { + button = frame_button_create(frame, + DATADIR "/weston/icon_window.png", + FRAME_STATUS_MENU, + FRAME_BUTTON_CLICK_DOWN); + if (!button) + goto free_frame; + } + + if (buttons & FRAME_BUTTON_CLOSE) { + button = frame_button_create(frame, + DATADIR "/weston/sign_close.png", + FRAME_STATUS_CLOSE, + FRAME_BUTTON_ALIGN_RIGHT | + FRAME_BUTTON_DECORATED); + if (!button) + goto free_frame; + } + + if (buttons & FRAME_BUTTON_MAXIMIZE) { + button = frame_button_create(frame, + DATADIR "/weston/sign_maximize.png", + FRAME_STATUS_MAXIMIZE, + FRAME_BUTTON_ALIGN_RIGHT | + FRAME_BUTTON_DECORATED); + if (!button) + goto free_frame; + } + + if (buttons & FRAME_BUTTON_MINIMIZE) { + button = frame_button_create(frame, + DATADIR "/weston/sign_minimize.png", + FRAME_STATUS_MINIMIZE, + FRAME_BUTTON_ALIGN_RIGHT | + FRAME_BUTTON_DECORATED); + if (!button) + goto free_frame; + } + + return frame; + +free_frame: + free(frame->title); + free(frame); + return NULL; +} + +void +frame_destroy(struct frame *frame) +{ + struct frame_button *button, *next; + + wl_list_for_each_safe(button, next, &frame->buttons, link) + frame_button_destroy(button); + + free(frame->title); + free(frame); +} + +int +frame_set_title(struct frame *frame, const char *title) +{ + char *dup = NULL; + + if (title) { + dup = strdup(title); + if (!dup) + return -1; + } + + free(frame->title); + frame->title = dup; + + frame->status |= FRAME_STATUS_REPAINT; + + return 0; +} + +void +frame_set_flag(struct frame *frame, enum frame_flag flag) +{ + if (flag & FRAME_FLAG_MAXIMIZED && !(frame->flags & FRAME_FLAG_MAXIMIZED)) + frame->geometry_dirty = 1; + + frame->flags |= flag; + frame->status |= FRAME_STATUS_REPAINT; +} + +void +frame_unset_flag(struct frame *frame, enum frame_flag flag) +{ + if (flag & FRAME_FLAG_MAXIMIZED && frame->flags & FRAME_FLAG_MAXIMIZED) + frame->geometry_dirty = 1; + + frame->flags &= ~flag; + frame->status |= FRAME_STATUS_REPAINT; +} + +void +frame_resize(struct frame *frame, int32_t width, int32_t height) +{ + frame->width = width; + frame->height = height; + + frame->geometry_dirty = 1; + frame->status |= FRAME_STATUS_REPAINT; +} + +void +frame_resize_inside(struct frame *frame, int32_t width, int32_t height) +{ + struct theme *t = frame->theme; + int decoration_width, decoration_height, titlebar_height; + + if (frame->title) + titlebar_height = t->titlebar_height; + else + titlebar_height = t->width; + + if (frame->flags & FRAME_FLAG_MAXIMIZED) { + decoration_width = t->width * 2; + decoration_height = t->width + titlebar_height; + } else { + decoration_width = (t->width + t->margin) * 2; + decoration_height = t->width + + titlebar_height + t->margin * 2; + } + + frame_resize(frame, width + decoration_width, + height + decoration_height); +} + +int32_t +frame_width(struct frame *frame) +{ + return frame->width; +} + +int32_t +frame_height(struct frame *frame) +{ + return frame->height; +} + +static void +frame_refresh_geometry(struct frame *frame) +{ + struct frame_button *button; + struct theme *t = frame->theme; + int x_l, x_r, y, w, h, titlebar_height; + int32_t decoration_width, decoration_height; + + if (!frame->geometry_dirty) + return; + + if (frame->title) + titlebar_height = t->titlebar_height; + else + titlebar_height = t->width; + + if (frame->flags & FRAME_FLAG_MAXIMIZED) { + decoration_width = t->width * 2; + decoration_height = t->width + titlebar_height; + + frame->interior.x = t->width; + frame->interior.y = titlebar_height; + frame->interior.width = frame->width - decoration_width; + frame->interior.height = frame->height - decoration_height; + + frame->opaque_margin = 0; + frame->shadow_margin = 0; + } else { + decoration_width = (t->width + t->margin) * 2; + decoration_height = t->width + titlebar_height + t->margin * 2; + + frame->interior.x = t->width + t->margin; + frame->interior.y = titlebar_height + t->margin; + frame->interior.width = frame->width - decoration_width; + frame->interior.height = frame->height - decoration_height; + + frame->opaque_margin = t->margin + t->frame_radius; + frame->shadow_margin = t->margin; + } + + x_r = frame->width - t->width - frame->shadow_margin; + x_l = t->width + frame->shadow_margin; + y = t->width + frame->shadow_margin; + wl_list_for_each(button, &frame->buttons, link) { + const int button_padding = 4; + w = cairo_image_surface_get_width(button->icon); + h = cairo_image_surface_get_height(button->icon); + + if (button->flags & FRAME_BUTTON_DECORATED) + w += 10; + + if (button->flags & FRAME_BUTTON_ALIGN_RIGHT) { + x_r -= w; + + button->allocation.x = x_r; + button->allocation.y = y; + button->allocation.width = w + 1; + button->allocation.height = h + 1; + + x_r -= button_padding; + } else { + button->allocation.x = x_l; + button->allocation.y = y; + button->allocation.width = w + 1; + button->allocation.height = h + 1; + + x_l += w; + x_l += button_padding; + } + } + + frame->geometry_dirty = 0; +} + +void +frame_interior(struct frame *frame, int32_t *x, int32_t *y, + int32_t *width, int32_t *height) +{ + frame_refresh_geometry(frame); + + if (x) + *x = frame->interior.x; + if (y) + *y = frame->interior.y; + if (width) + *width = frame->interior.width; + if (height) + *height = frame->interior.height; +} + +void +frame_input_rect(struct frame *frame, int32_t *x, int32_t *y, + int32_t *width, int32_t *height) +{ + frame_refresh_geometry(frame); + + if (x) + *x = frame->shadow_margin; + if (y) + *y = frame->shadow_margin; + if (width) + *width = frame->width - frame->shadow_margin * 2; + if (height) + *height = frame->height - frame->shadow_margin * 2; +} + +void +frame_opaque_rect(struct frame *frame, int32_t *x, int32_t *y, + int32_t *width, int32_t *height) +{ + frame_refresh_geometry(frame); + + if (x) + *x = frame->opaque_margin; + if (y) + *y = frame->opaque_margin; + if (width) + *width = frame->width - frame->opaque_margin * 2; + if (height) + *height = frame->height - frame->opaque_margin * 2; +} + +uint32_t +frame_status(struct frame *frame) +{ + return frame->status; +} + +void +frame_status_clear(struct frame *frame, enum frame_status status) +{ + frame->status &= ~status; +} + +static struct frame_button * +frame_find_button(struct frame *frame, int x, int y) +{ + struct frame_button *button; + int rel_x, rel_y; + + wl_list_for_each(button, &frame->buttons, link) { + rel_x = x - button->allocation.x; + rel_y = y - button->allocation.y; + + if (0 <= rel_x && rel_x < button->allocation.width && + 0 <= rel_y && rel_y < button->allocation.height) + return button; + } + + return NULL; +} + +enum theme_location +frame_pointer_enter(struct frame *frame, void *data, int x, int y) +{ + return frame_pointer_motion(frame, data, x, y); +} + +enum theme_location +frame_pointer_motion(struct frame *frame, void *data, int x, int y) +{ + struct frame_pointer *pointer = frame_pointer_get(frame, data); + struct frame_button *button = frame_find_button(frame, x, y); + enum theme_location location; + + location = theme_get_location(frame->theme, x, y, + frame->width, frame->height, + frame->flags & FRAME_FLAG_MAXIMIZED ? + THEME_FRAME_MAXIMIZED : 0); + if (!pointer) + return location; + + pointer->x = x; + pointer->y = y; + + if (pointer->hover_button == button) + return location; + + if (pointer->hover_button) + frame_button_leave(pointer->hover_button, pointer); + + pointer->hover_button = button; + + if (pointer->hover_button) + frame_button_enter(pointer->hover_button); + + return location; +} + +static void +frame_pointer_button_destroy(struct frame_pointer_button *button) +{ + wl_list_remove(&button->link); + free(button); +} + +static void +frame_pointer_button_press(struct frame *frame, struct frame_pointer *pointer, + struct frame_pointer_button *button) +{ + if (button->button == BTN_RIGHT) { + if (button->press_location == THEME_LOCATION_TITLEBAR) + frame->status |= FRAME_STATUS_MENU; + + frame_pointer_button_destroy(button); + + } else if (button->button == BTN_LEFT) { + if (pointer->hover_button) { + frame_button_press(pointer->hover_button); + } else { + switch (button->press_location) { + case THEME_LOCATION_TITLEBAR: + frame->status |= FRAME_STATUS_MOVE; + + frame_pointer_button_destroy(button); + break; + case THEME_LOCATION_RESIZING_TOP: + case THEME_LOCATION_RESIZING_BOTTOM: + case THEME_LOCATION_RESIZING_LEFT: + case THEME_LOCATION_RESIZING_RIGHT: + case THEME_LOCATION_RESIZING_TOP_LEFT: + case THEME_LOCATION_RESIZING_TOP_RIGHT: + case THEME_LOCATION_RESIZING_BOTTOM_LEFT: + case THEME_LOCATION_RESIZING_BOTTOM_RIGHT: + frame->status |= FRAME_STATUS_RESIZE; + + frame_pointer_button_destroy(button); + break; + default: + break; + } + } + } +} + +static void +frame_pointer_button_release(struct frame *frame, struct frame_pointer *pointer, + struct frame_pointer_button *button) +{ + if (button->button == BTN_LEFT && button->frame_button) { + if (button->frame_button == pointer->hover_button) + frame_button_release(button->frame_button); + else + frame_button_cancel(button->frame_button); + } +} + +static void +frame_pointer_button_cancel(struct frame *frame, struct frame_pointer *pointer, + struct frame_pointer_button *button) +{ + if (button->frame_button) + frame_button_cancel(button->frame_button); +} + +void +frame_pointer_leave(struct frame *frame, void *data) +{ + struct frame_pointer *pointer = frame_pointer_get(frame, data); + struct frame_pointer_button *button, *next; + if (!pointer) + return; + + if (pointer->hover_button) + frame_button_leave(pointer->hover_button, pointer); + + wl_list_for_each_safe(button, next, &pointer->down_buttons, link) { + frame_pointer_button_cancel(frame, pointer, button); + frame_pointer_button_destroy(button); + } + + frame_pointer_destroy(pointer); +} + +enum theme_location +frame_pointer_button(struct frame *frame, void *data, + uint32_t btn, enum frame_button_state state) +{ + struct frame_pointer *pointer = frame_pointer_get(frame, data); + struct frame_pointer_button *button; + enum theme_location location; + + location = theme_get_location(frame->theme, pointer->x, pointer->y, + frame->width, frame->height, + frame->flags & FRAME_FLAG_MAXIMIZED ? + THEME_FRAME_MAXIMIZED : 0); + + if (!pointer) + return location; + + if (state == FRAME_BUTTON_PRESSED) { + button = malloc(sizeof *button); + if (!button) + return location; + + button->button = btn; + button->press_location = location; + button->frame_button = pointer->hover_button; + wl_list_insert(&pointer->down_buttons, &button->link); + + frame_pointer_button_press(frame, pointer, button); + } else if (state == FRAME_BUTTON_RELEASED) { + button = NULL; + wl_list_for_each(button, &pointer->down_buttons, link) + if (button->button == btn) + break; + /* Make sure we didn't hit the end */ + if (&button->link == &pointer->down_buttons) + return location; + + location = button->press_location; + frame_pointer_button_release(frame, pointer, button); + frame_pointer_button_destroy(button); + } + + return location; +} + +void +frame_touch_down(struct frame *frame, void *data, int32_t id, int x, int y) +{ + struct frame_touch *touch = frame_touch_get(frame, data); + struct frame_button *button = frame_find_button(frame, x, y); + enum theme_location location; + + if (id > 0) + return; + + if (button) { + touch->button = button; + frame_button_press(touch->button); + return; + } + + location = theme_get_location(frame->theme, x, y, + frame->width, frame->height, + frame->flags & FRAME_FLAG_MAXIMIZED ? + THEME_FRAME_MAXIMIZED : 0); + + switch (location) { + case THEME_LOCATION_TITLEBAR: + frame->status |= FRAME_STATUS_MOVE; + break; + case THEME_LOCATION_RESIZING_TOP: + case THEME_LOCATION_RESIZING_BOTTOM: + case THEME_LOCATION_RESIZING_LEFT: + case THEME_LOCATION_RESIZING_RIGHT: + case THEME_LOCATION_RESIZING_TOP_LEFT: + case THEME_LOCATION_RESIZING_TOP_RIGHT: + case THEME_LOCATION_RESIZING_BOTTOM_LEFT: + case THEME_LOCATION_RESIZING_BOTTOM_RIGHT: + frame->status |= FRAME_STATUS_RESIZE; + break; + default: + break; + } +} + +void +frame_touch_up(struct frame *frame, void *data, int32_t id) +{ + struct frame_touch *touch = frame_touch_get(frame, data); + + if (id > 0) + return; + + if (touch->button) { + frame_button_release(touch->button); + frame_touch_destroy(touch); + return; + } +} + +void +frame_repaint(struct frame *frame, cairo_t *cr) +{ + struct frame_button *button; + uint32_t flags = 0; + + frame_refresh_geometry(frame); + + if (frame->flags & FRAME_FLAG_MAXIMIZED) + flags |= THEME_FRAME_MAXIMIZED; + + if (frame->flags & FRAME_FLAG_ACTIVE) + flags |= THEME_FRAME_ACTIVE; + + cairo_save(cr); + theme_render_frame(frame->theme, cr, frame->width, frame->height, + frame->title, flags); + cairo_restore(cr); + + wl_list_for_each(button, &frame->buttons, link) + frame_button_repaint(button, cr); + + frame_status_clear(frame, FRAME_STATUS_REPAINT); +} diff --git a/shared/os-compatibility.c b/shared/os-compatibility.c index 4f96dd4c..611e7c80 100644 --- a/shared/os-compatibility.c +++ b/shared/os-compatibility.c @@ -132,6 +132,12 @@ create_tmpfile_cloexec(char *tmpname) * The file is suitable for buffer sharing between processes by * transmitting the file descriptor over Unix sockets using the * SCM_RIGHTS methods. + * + * If the C library implements posix_fallocate(), it is used to + * guarantee that disk space is available for the file at the + * given size. If disk space is insufficent, errno is set to ENOSPC. + * If posix_fallocate() is not supported, program may receive + * SIGBUS on accessing mmap()'ed file contents instead. */ int os_create_anonymous_file(off_t size) @@ -140,6 +146,7 @@ os_create_anonymous_file(off_t size) const char *path; char *name; int fd; + int ret; path = getenv("XDG_RUNTIME_DIR"); if (!path) { @@ -161,10 +168,20 @@ os_create_anonymous_file(off_t size) if (fd < 0) return -1; - if (ftruncate(fd, size) < 0) { +#ifdef HAVE_POSIX_FALLOCATE + ret = posix_fallocate(fd, 0, size); + if (ret != 0) { close(fd); + errno = ret; return -1; } +#else + ret = ftruncate(fd, size); + if (ret < 0) { + close(fd); + return -1; + } +#endif return fd; } diff --git a/src/.gitignore b/src/.gitignore index 539150d4..024594c8 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -7,18 +7,11 @@ screenshooter-server-protocol.h spring-tool text-cursor-position-protocol.c text-cursor-position-server-protocol.h -tablet-shell-protocol.c -tablet-shell-server-protocol.h -xserver-protocol.c -xserver-server-protocol.h -desktop-shell-protocol.c -desktop-shell-server-protocol.h text-protocol.c text-server-protocol.h workspaces-protocol.c workspaces-server-protocol.h input-method-protocol.c input-method-server-protocol.h -subsurface-server-protocol.h -subsurface-protocol.c - +scaler-server-protocol.h +scaler-protocol.c diff --git a/src/Makefile.am b/src/Makefile.am index 749c074a..446639cc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,25 +36,38 @@ weston_SOURCES = \ input-method-server-protocol.h \ workspaces-protocol.c \ workspaces-server-protocol.h \ - subsurface-protocol.c \ - subsurface-server-protocol.h \ + scaler-protocol.c \ + scaler-server-protocol.h \ bindings.c \ animation.c \ - gl-renderer.h \ noop-renderer.c \ pixman-renderer.c \ pixman-renderer.h \ ../shared/matrix.c \ ../shared/matrix.h \ ../shared/zalloc.h \ - weston-launch.h \ weston-egl-ext.h -if ENABLE_EGL -weston_SOURCES += \ - gl-renderer.c \ - vertex-clipping.c \ - vertex-clipping.h +noinst_LTLIBRARIES = \ + libsession-helper.la + +libsession_helper_la_SOURCES = \ + weston-launch.h \ + launcher-util.c \ + launcher-util.h +libsession_helper_la_CFLAGS = $(GCC_CFLAGS) $(LIBDRM_CFLAGS) $(PIXMAN_CFLAGS) $(COMPOSITOR_CFLAGS) +libsession_helper_la_LIBADD = $(LIBDRM_LIBS) + +if ENABLE_DBUS +if HAVE_SYSTEMD_LOGIN +libsession_helper_la_SOURCES += \ + dbus.h \ + dbus.c \ + logind-util.h \ + logind-util.c +libsession_helper_la_CFLAGS += $(SYSTEMD_LOGIN_CFLAGS) $(DBUS_CFLAGS) +libsession_helper_la_LIBADD += $(SYSTEMD_LOGIN_LIBS) $(DBUS_LIBS) +endif endif git-version.h : .FORCE @@ -64,20 +77,16 @@ git-version.h : .FORCE .FORCE : -if ENABLE_XWAYLAND -SUBDIRS = xwayland -endif - -DIST_SUBDIRS = xwayland - - if BUILD_WESTON_LAUNCH weston_launch = weston-launch weston_launch_SOURCES = weston-launch.c weston-launch.h -weston_launch_CFLAGS= $(GCC_CFLAGS) -weston_launch_CPPFLAGS = $(WESTON_LAUNCH_CFLAGS) $(SYSTEMD_LOGIN_CFLAGS) \ - -DBINDIR='"$(bindir)"' -weston_launch_LDADD = $(WESTON_LAUNCH_LIBS) $(SYSTEMD_LOGIN_LIBS) +weston_launch_CPPFLAGS = -DBINDIR='"$(bindir)"' +weston_launch_CFLAGS= \ + $(GCC_CFLAGS) \ + $(PAM_CFLAGS) \ + $(SYSTEMD_LOGIN_CFLAGS) \ + $(LIBDRM_CFLAGS) +weston_launch_LDADD = $(PAM_LIBS) $(SYSTEMD_LOGIN_LIBS) $(LIBDRM_LIBS) if ENABLE_SETUID_INSTALL install-exec-hook: @@ -100,10 +109,9 @@ westoninclude_HEADERS = \ moduledir = $(libdir)/weston module_LTLIBRARIES = \ - $(desktop_shell) \ - $(tablet_shell) \ $(cms_static) \ $(cms_colord) \ + $(gl_renderer) \ $(x11_backend) \ $(drm_backend) \ $(wayland_backend) \ @@ -111,14 +119,27 @@ module_LTLIBRARIES = \ $(fbdev_backend) \ $(rdp_backend) -noinst_LTLIBRARIES = - if INSTALL_RPI_COMPOSITOR module_LTLIBRARIES += $(rpi_backend) else noinst_LTLIBRARIES += $(rpi_backend) endif +if ENABLE_EGL +gl_renderer = gl-renderer.la +gl_renderer_la_LDFLAGS = -module -avoid-version +gl_renderer_la_LIBADD = $(COMPOSITOR_LIBS) $(EGL_LIBS) +gl_renderer_la_CFLAGS = \ + $(COMPOSITOR_CFLAGS) \ + $(EGL_CFLAGS) \ + $(GCC_CFLAGS) +gl_renderer_la_SOURCES = \ + gl-renderer.h \ + gl-renderer.c \ + vertex-clipping.c \ + vertex-clipping.h +endif + if ENABLE_X11_COMPOSITOR x11_backend = x11-backend.la x11_backend_la_LDFLAGS = -module -avoid-version @@ -126,6 +147,7 @@ x11_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(X11_COMPOSITOR_LIBS) \ ../shared/libshared-cairo.la x11_backend_la_CFLAGS = \ $(COMPOSITOR_CFLAGS) \ + $(EGL_CFLAGS) \ $(PIXMAN_CFLAGS) \ $(CAIRO_CFLAGS) \ $(X11_COMPOSITOR_CFLAGS) \ @@ -136,10 +158,14 @@ endif if ENABLE_DRM_COMPOSITOR drm_backend = drm-backend.la drm_backend_la_LDFLAGS = -module -avoid-version -drm_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(DRM_COMPOSITOR_LIBS) \ - ../shared/libshared.la -lrt +drm_backend_la_LIBADD = \ + $(COMPOSITOR_LIBS) \ + $(DRM_COMPOSITOR_LIBS) \ + ../shared/libshared.la -lrt \ + libsession-helper.la drm_backend_la_CFLAGS = \ $(COMPOSITOR_CFLAGS) \ + $(EGL_CFLAGS) \ $(DRM_COMPOSITOR_CFLAGS) \ $(GCC_CFLAGS) drm_backend_la_SOURCES = \ @@ -149,8 +175,6 @@ drm_backend_la_SOURCES = \ evdev.c \ evdev.h \ evdev-touchpad.c \ - launcher-util.c \ - launcher-util.h \ libbacklight.c \ libbacklight.h @@ -164,10 +188,13 @@ endif if ENABLE_WAYLAND_COMPOSITOR wayland_backend = wayland-backend.la wayland_backend_la_LDFLAGS = -module -avoid-version -wayland_backend_la_LIBADD = $(COMPOSITOR_LIBS) $(WAYLAND_COMPOSITOR_LIBS) \ +wayland_backend_la_LIBADD = \ + $(COMPOSITOR_LIBS) \ + $(WAYLAND_COMPOSITOR_LIBS) \ ../shared/libshared-cairo.la wayland_backend_la_CFLAGS = \ $(COMPOSITOR_CFLAGS) \ + $(EGL_CFLAGS) \ $(PIXMAN_CFLAGS) \ $(CAIRO_CFLAGS) \ $(WAYLAND_COMPOSITOR_CFLAGS) \ @@ -181,6 +208,7 @@ rpi_backend_la_LDFLAGS = -module -avoid-version rpi_backend_la_LIBADD = $(COMPOSITOR_LIBS) \ $(RPI_COMPOSITOR_LIBS) \ $(RPI_BCM_HOST_LIBS) \ + libsession-helper.la \ ../shared/libshared.la rpi_backend_la_CFLAGS = \ $(GCC_CFLAGS) \ @@ -192,11 +220,17 @@ rpi_backend_la_SOURCES = \ rpi-renderer.c \ rpi-renderer.h \ rpi-bcm-stubs.h \ - launcher-util.c \ - launcher-util.h \ + udev-seat.c \ + udev-seat.h \ evdev.c \ evdev.h \ evdev-touchpad.c + +if ENABLE_EGL +rpi_backend_la_LIBADD += $(EGL_LIBS) +rpi_backend_la_CFLAGS += $(EGL_CFLAGS) +endif + endif if ENABLE_HEADLESS_COMPOSITOR @@ -216,9 +250,11 @@ fbdev_backend_la_LDFLAGS = -module -avoid-version fbdev_backend_la_LIBADD = \ $(COMPOSITOR_LIBS) \ $(FBDEV_COMPOSITOR_LIBS) \ + libsession-helper.la \ ../shared/libshared.la fbdev_backend_la_CFLAGS = \ $(COMPOSITOR_CFLAGS) \ + $(EGL_CFLAGS) \ $(FBDEV_COMPOSITOR_CFLAGS) \ $(PIXMAN_CFLAGS) \ $(GCC_CFLAGS) @@ -228,9 +264,7 @@ fbdev_backend_la_SOURCES = \ udev-seat.h \ evdev.c \ evdev.h \ - evdev-touchpad.c \ - launcher-util.c \ - launcher-util.h + evdev-touchpad.c endif if ENABLE_RDP_COMPOSITOR @@ -246,29 +280,6 @@ rdp_backend_la_CFLAGS = \ rdp_backend_la_SOURCES = compositor-rdp.c endif -if ENABLE_DESKTOP_SHELL -desktop_shell = desktop-shell.la -desktop_shell_la_LDFLAGS = -module -avoid-version -desktop_shell_la_LIBADD = $(COMPOSITOR_LIBS) \ - ../shared/libshared.la -desktop_shell_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) -desktop_shell_la_SOURCES = \ - shell.c \ - desktop-shell-protocol.c \ - desktop-shell-server-protocol.h -endif - -if ENABLE_TABLET_SHELL -tablet_shell = tablet-shell.la -tablet_shell_la_LDFLAGS = -module -avoid-version -tablet_shell_la_LIBADD = $(COMPOSITOR_LIBS) -tablet_shell_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) -tablet_shell_la_SOURCES = \ - tablet-shell.c \ - tablet-shell-protocol.c \ - tablet-shell-server-protocol.h -endif - if HAVE_LCMS cms_static = cms-static.la cms_static_la_LDFLAGS = -module -avoid-version @@ -306,18 +317,14 @@ BUILT_SOURCES = \ screenshooter-protocol.c \ text-cursor-position-server-protocol.h \ text-cursor-position-protocol.c \ - tablet-shell-protocol.c \ - tablet-shell-server-protocol.h \ - desktop-shell-protocol.c \ - desktop-shell-server-protocol.h \ text-protocol.c \ text-server-protocol.h \ input-method-protocol.c \ input-method-server-protocol.h \ workspaces-server-protocol.h \ workspaces-protocol.c \ - subsurface-server-protocol.h \ - subsurface-protocol.c \ + scaler-server-protocol.h \ + scaler-protocol.c \ git-version.h CLEANFILES = $(BUILT_SOURCES) diff --git a/src/animation.c b/src/animation.c index 2c47ae7e..ee120367 100644 --- a/src/animation.c +++ b/src/animation.c @@ -34,7 +34,7 @@ WL_EXPORT void weston_spring_init(struct weston_spring *spring, - double k, double current, double target) + double k, double current, double target) { spring->k = k; spring->friction = 400.0; @@ -114,52 +114,53 @@ weston_spring_done(struct weston_spring *spring) fabs(spring->current - spring->target) < 0.002; } -typedef void (*weston_surface_animation_frame_func_t)(struct weston_surface_animation *animation); +typedef void (*weston_view_animation_frame_func_t)(struct weston_view_animation *animation); -struct weston_surface_animation { - struct weston_surface *surface; +struct weston_view_animation { + struct weston_view *view; struct weston_animation animation; struct weston_spring spring; struct weston_transform transform; struct wl_listener listener; float start, stop; - weston_surface_animation_frame_func_t frame; - weston_surface_animation_frame_func_t reset; - weston_surface_animation_done_func_t done; + weston_view_animation_frame_func_t frame; + weston_view_animation_frame_func_t reset; + weston_view_animation_done_func_t done; void *data; + void *private; }; -static void -weston_surface_animation_destroy(struct weston_surface_animation *animation) +WL_EXPORT void +weston_view_animation_destroy(struct weston_view_animation *animation) { wl_list_remove(&animation->animation.link); wl_list_remove(&animation->listener.link); wl_list_remove(&animation->transform.link); if (animation->reset) animation->reset(animation); - weston_surface_geometry_dirty(animation->surface); + weston_view_geometry_dirty(animation->view); if (animation->done) animation->done(animation, animation->data); free(animation); } static void -handle_animation_surface_destroy(struct wl_listener *listener, void *data) +handle_animation_view_destroy(struct wl_listener *listener, void *data) { - struct weston_surface_animation *animation = + struct weston_view_animation *animation = container_of(listener, - struct weston_surface_animation, listener); + struct weston_view_animation, listener); - weston_surface_animation_destroy(animation); + weston_view_animation_destroy(animation); } static void -weston_surface_animation_frame(struct weston_animation *base, - struct weston_output *output, uint32_t msecs) +weston_view_animation_frame(struct weston_animation *base, + struct weston_output *output, uint32_t msecs) { - struct weston_surface_animation *animation = + struct weston_view_animation *animation = container_of(base, - struct weston_surface_animation, animation); + struct weston_view_animation, animation); if (base->frame_counter <= 1) animation->spring.timestamp = msecs; @@ -167,69 +168,71 @@ weston_surface_animation_frame(struct weston_animation *base, weston_spring_update(&animation->spring, msecs); if (weston_spring_done(&animation->spring)) { - weston_compositor_schedule_repaint(animation->surface->compositor); - weston_surface_animation_destroy(animation); + weston_view_schedule_repaint(animation->view); + weston_view_animation_destroy(animation); return; } if (animation->frame) animation->frame(animation); - weston_surface_geometry_dirty(animation->surface); - weston_compositor_schedule_repaint(animation->surface->compositor); + weston_view_geometry_dirty(animation->view); + weston_view_schedule_repaint(animation->view); } -static struct weston_surface_animation * -weston_surface_animation_run(struct weston_surface *surface, - float start, float stop, - weston_surface_animation_frame_func_t frame, - weston_surface_animation_frame_func_t reset, - weston_surface_animation_done_func_t done, - void *data) +static struct weston_view_animation * +weston_view_animation_run(struct weston_view *view, + float start, float stop, + weston_view_animation_frame_func_t frame, + weston_view_animation_frame_func_t reset, + weston_view_animation_done_func_t done, + void *data, + void *private) { - struct weston_surface_animation *animation; + struct weston_view_animation *animation; animation = malloc(sizeof *animation); if (!animation) return NULL; - animation->surface = surface; + animation->view = view; animation->frame = frame; animation->reset = reset; animation->done = done; animation->data = data; animation->start = start; animation->stop = stop; + animation->private = private; weston_matrix_init(&animation->transform.matrix); - wl_list_insert(&surface->geometry.transformation_list, + wl_list_insert(&view->geometry.transformation_list, &animation->transform.link); weston_spring_init(&animation->spring, 200.0, 0.0, 1.0); animation->spring.friction = 700; animation->animation.frame_counter = 0; - animation->animation.frame = weston_surface_animation_frame; - weston_surface_animation_frame(&animation->animation, NULL, 0); + animation->animation.frame = weston_view_animation_frame; + weston_view_animation_frame(&animation->animation, NULL, 0); - animation->listener.notify = handle_animation_surface_destroy; - wl_signal_add(&surface->destroy_signal, &animation->listener); + animation->listener.notify = handle_animation_view_destroy; + wl_signal_add(&view->destroy_signal, &animation->listener); - wl_list_insert(&surface->output->animation_list, + wl_list_insert(&view->output->animation_list, &animation->animation.link); return animation; } static void -reset_alpha(struct weston_surface_animation *animation) +reset_alpha(struct weston_view_animation *animation) { - struct weston_surface *surface = animation->surface; + struct weston_view *view = animation->view; - surface->alpha = animation->stop; + view->alpha = animation->stop; } static void -zoom_frame(struct weston_surface_animation *animation) +zoom_frame(struct weston_view_animation *animation) { - struct weston_surface *es = animation->surface; + struct weston_view *es = animation->view; float scale; scale = animation->start + @@ -237,27 +240,27 @@ zoom_frame(struct weston_surface_animation *animation) animation->spring.current; weston_matrix_init(&animation->transform.matrix); weston_matrix_translate(&animation->transform.matrix, - -0.5f * es->geometry.width, - -0.5f * es->geometry.height, 0); + -0.5f * es->surface->width, + -0.5f * es->surface->height, 0); weston_matrix_scale(&animation->transform.matrix, scale, scale, scale); weston_matrix_translate(&animation->transform.matrix, - 0.5f * es->geometry.width, - 0.5f * es->geometry.height, 0); + 0.5f * es->surface->width, + 0.5f * es->surface->height, 0); es->alpha = animation->spring.current; if (es->alpha > 1.0) es->alpha = 1.0; } -WL_EXPORT struct weston_surface_animation * -weston_zoom_run(struct weston_surface *surface, float start, float stop, - weston_surface_animation_done_func_t done, void *data) +WL_EXPORT struct weston_view_animation * +weston_zoom_run(struct weston_view *view, float start, float stop, + weston_view_animation_done_func_t done, void *data) { - struct weston_surface_animation *zoom; + struct weston_view_animation *zoom; - zoom = weston_surface_animation_run(surface, start, stop, - zoom_frame, reset_alpha, - done, data); + zoom = weston_view_animation_run(view, start, stop, + zoom_frame, reset_alpha, + done, data, NULL); weston_spring_init(&zoom->spring, 300.0, start, stop); zoom->spring.friction = 1400; @@ -267,45 +270,85 @@ weston_zoom_run(struct weston_surface *surface, float start, float stop, } static void -fade_frame(struct weston_surface_animation *animation) +fade_frame(struct weston_view_animation *animation) { if (animation->spring.current > 0.999) - animation->surface->alpha = 1; + animation->view->alpha = 1; else if (animation->spring.current < 0.001 ) - animation->surface->alpha = 0; + animation->view->alpha = 0; else - animation->surface->alpha = animation->spring.current; + animation->view->alpha = animation->spring.current; } -WL_EXPORT struct weston_surface_animation * -weston_fade_run(struct weston_surface *surface, +WL_EXPORT struct weston_view_animation * +weston_fade_run(struct weston_view *view, float start, float end, float k, - weston_surface_animation_done_func_t done, void *data) + weston_view_animation_done_func_t done, void *data) { - struct weston_surface_animation *fade; + struct weston_view_animation *fade; - fade = weston_surface_animation_run(surface, 0, end, - fade_frame, reset_alpha, - done, data); + fade = weston_view_animation_run(view, 0, end, + fade_frame, reset_alpha, + done, data, NULL); weston_spring_init(&fade->spring, k, start, end); fade->spring.friction = 1400; fade->spring.previous = -(end - start) * 0.03; - surface->alpha = start; + view->alpha = start; return fade; } WL_EXPORT void -weston_fade_update(struct weston_surface_animation *fade, float target) +weston_fade_update(struct weston_view_animation *fade, float target) { fade->spring.target = target; } static void -slide_frame(struct weston_surface_animation *animation) +stable_fade_frame(struct weston_view_animation *animation) +{ + struct weston_view *back_view; + + if (animation->spring.current > 0.999) + animation->view->alpha = 1; + else if (animation->spring.current < 0.001 ) + animation->view->alpha = 0; + else + animation->view->alpha = animation->spring.current; + + back_view = (struct weston_view *) animation->private; + back_view->alpha = + (animation->spring.target - animation->view->alpha) / + (1.0 - animation->view->alpha); + weston_view_geometry_dirty(back_view); +} + +WL_EXPORT struct weston_view_animation * +weston_stable_fade_run(struct weston_view *front_view, float start, + struct weston_view *back_view, float end, + weston_view_animation_done_func_t done, void *data) +{ + struct weston_view_animation *fade; + + fade = weston_view_animation_run(front_view, 0, 0, + stable_fade_frame, NULL, + done, data, back_view); + + + weston_spring_init(&fade->spring, 400, start, end); + fade->spring.friction = 1150; + + front_view->alpha = start; + back_view->alpha = end; + + return fade; +} + +static void +slide_frame(struct weston_view_animation *animation) { float scale; @@ -316,15 +359,15 @@ slide_frame(struct weston_surface_animation *animation) weston_matrix_translate(&animation->transform.matrix, 0, scale, 0); } -WL_EXPORT struct weston_surface_animation * -weston_slide_run(struct weston_surface *surface, float start, float stop, - weston_surface_animation_done_func_t done, void *data) +WL_EXPORT struct weston_view_animation * +weston_slide_run(struct weston_view *view, float start, float stop, + weston_view_animation_done_func_t done, void *data) { - struct weston_surface_animation *animation; + struct weston_view_animation *animation; - animation = weston_surface_animation_run(surface, start, stop, - slide_frame, NULL, done, - data); + animation = weston_view_animation_run(view, start, stop, + slide_frame, NULL, done, + data, NULL); if (!animation) return NULL; @@ -334,3 +377,65 @@ weston_slide_run(struct weston_surface *surface, float start, float stop, return animation; } + +struct weston_move_animation { + int dx; + int dy; + int reverse; + weston_view_animation_done_func_t done; +}; + +static void +move_frame(struct weston_view_animation *animation) +{ + struct weston_move_animation *move = animation->private; + float scale; + float progress = animation->spring.current; + + if (move->reverse) + progress = 1.0 - progress; + + scale = animation->start + + (animation->stop - animation->start) * + progress; + weston_matrix_init(&animation->transform.matrix); + weston_matrix_scale(&animation->transform.matrix, scale, scale, 1.0f); + weston_matrix_translate(&animation->transform.matrix, + move->dx * progress, move->dy * progress, + 0); +} + +static void +move_done(struct weston_view_animation *animation, void *data) +{ + struct weston_move_animation *move = animation->private; + + if (move->done) + move->done(animation, data); + + free(move); +} + +WL_EXPORT struct weston_view_animation * +weston_move_scale_run(struct weston_view *view, int dx, int dy, + float start, float end, int reverse, + weston_view_animation_done_func_t done, void *data) +{ + struct weston_move_animation *move; + struct weston_view_animation *animation; + + move = malloc(sizeof(*move)); + if (!move) + return NULL; + move->dx = dx; + move->dy = dy; + move->reverse = reverse; + move->done = done; + + animation = weston_view_animation_run(view, start, end, move_frame, + NULL, move_done, data, move); + animation->spring.k = 400; + animation->spring.friction = 1150; + + return animation; +} diff --git a/src/bindings.c b/src/bindings.c index 7cbded92..7d30024a 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -75,6 +75,24 @@ weston_compositor_add_key_binding(struct weston_compositor *compositor, return binding; } +WL_EXPORT struct weston_binding * +weston_compositor_add_modifier_binding(struct weston_compositor *compositor, + uint32_t modifier, + weston_modifier_binding_handler_t handler, + void *data) +{ + struct weston_binding *binding; + + binding = weston_compositor_add_binding(compositor, 0, 0, 0, + modifier, handler, data); + if (binding == NULL) + return NULL; + + wl_list_insert(compositor->modifier_binding_list.prev, &binding->link); + + return binding; +} + WL_EXPORT struct weston_binding * weston_compositor_add_button_binding(struct weston_compositor *compositor, uint32_t button, uint32_t modifier, @@ -248,6 +266,10 @@ weston_compositor_run_key_binding(struct weston_compositor *compositor, if (state == WL_KEYBOARD_KEY_STATE_RELEASED) return; + /* Invalidate all active modifier bindings. */ + wl_list_for_each(b, &compositor->modifier_binding_list, link) + b->key = key; + wl_list_for_each(b, &compositor->key_binding_list, link) { if (b->key == key && b->modifier == seat->modifier_state) { weston_key_binding_handler_t handler = b->handler; @@ -263,6 +285,37 @@ weston_compositor_run_key_binding(struct weston_compositor *compositor, } } +WL_EXPORT void +weston_compositor_run_modifier_binding(struct weston_compositor *compositor, + struct weston_seat *seat, + enum weston_keyboard_modifier modifier, + enum wl_keyboard_key_state state) +{ + struct weston_binding *b; + + if (seat->keyboard->grab != &seat->keyboard->default_grab) + return; + + wl_list_for_each(b, &compositor->modifier_binding_list, link) { + weston_modifier_binding_handler_t handler = b->handler; + + if (b->modifier != modifier) + continue; + + /* Prime the modifier binding. */ + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { + b->key = 0; + continue; + } + /* Ignore the binding if a key was pressed in between. */ + else if (b->key != 0) { + return; + } + + handler(seat, modifier, b->data); + } +} + WL_EXPORT void weston_compositor_run_button_binding(struct weston_compositor *compositor, struct weston_seat *seat, @@ -274,6 +327,10 @@ weston_compositor_run_button_binding(struct weston_compositor *compositor, if (state == WL_POINTER_BUTTON_STATE_RELEASED) return; + /* Invalidate all active modifier bindings. */ + wl_list_for_each(b, &compositor->modifier_binding_list, link) + b->key = button; + wl_list_for_each(b, &compositor->button_binding_list, link) { if (b->button == button && b->modifier == seat->modifier_state) { weston_button_binding_handler_t handler = b->handler; @@ -289,7 +346,7 @@ weston_compositor_run_touch_binding(struct weston_compositor *compositor, { struct weston_binding *b; - if (seat->num_tp != 1 || touch_type != WL_TOUCH_DOWN) + if (seat->touch->num_tp != 1 || touch_type != WL_TOUCH_DOWN) return; wl_list_for_each(b, &compositor->touch_binding_list, link) { @@ -308,6 +365,10 @@ weston_compositor_run_axis_binding(struct weston_compositor *compositor, { struct weston_binding *b; + /* Invalidate all active modifier bindings. */ + wl_list_for_each(b, &compositor->modifier_binding_list, link) + b->key = axis; + wl_list_for_each(b, &compositor->axis_binding_list, link) { if (b->axis == axis && b->modifier == seat->modifier_state) { weston_axis_binding_handler_t handler = b->handler; diff --git a/src/compositor-drm.c b/src/compositor-drm.c index 4f015d11..136d517e 100644 --- a/src/compositor-drm.c +++ b/src/compositor-drm.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -155,7 +156,7 @@ struct drm_output { struct gbm_bo *cursor_bo[2]; struct weston_plane cursor_plane; struct weston_plane fb_plane; - struct weston_surface *cursor_surface; + struct weston_view *cursor_view; int current_cursor; struct drm_fb *current, *next; struct backlight *backlight; @@ -194,6 +195,15 @@ struct drm_sprite { uint32_t formats[]; }; +struct drm_parameters { + int connector; + int tty; + int use_pixman; + const char *seat_id; +}; + +static struct gl_renderer_interface *gl_renderer; + static const char default_seat[] = "seat0"; static void @@ -402,7 +412,7 @@ drm_output_release_fb(struct drm_output *output, struct drm_fb *fb) gbm_bo_destroy(fb->bo); else gbm_surface_release_buffer(output->surface, - output->current->bo); + fb->bo); } } @@ -410,52 +420,51 @@ static uint32_t drm_output_check_scanout_format(struct drm_output *output, struct weston_surface *es, struct gbm_bo *bo) { + struct drm_compositor *c = + (struct drm_compositor *) output->base.compositor; uint32_t format; pixman_region32_t r; format = gbm_bo_get_format(bo); - switch (format) { - case GBM_FORMAT_XRGB8888: - return format; - case GBM_FORMAT_ARGB8888: - /* We can only scanout an ARGB buffer if the surface's - * opaque region covers the whole output */ + if (format == GBM_FORMAT_ARGB8888) { + /* We can scanout an ARGB buffer if the surface's + * opaque region covers the whole output, but we have + * to use XRGB as the KMS format code. */ pixman_region32_init(&r); pixman_region32_subtract(&r, &output->base.region, &es->opaque); if (!pixman_region32_not_empty(&r)) format = GBM_FORMAT_XRGB8888; - else - format = 0; pixman_region32_fini(&r); + } + if (c->format == format) return format; - default: - return 0; - } + + return 0; } static struct weston_plane * -drm_output_prepare_scanout_surface(struct weston_output *_output, - struct weston_surface *es) +drm_output_prepare_scanout_view(struct weston_output *_output, + struct weston_view *ev) { struct drm_output *output = (struct drm_output *) _output; struct drm_compositor *c = (struct drm_compositor *) output->base.compositor; - struct weston_buffer *buffer = es->buffer_ref.buffer; + struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; struct gbm_bo *bo; uint32_t format; - if (es->geometry.x != output->base.x || - es->geometry.y != output->base.y || + if (ev->geometry.x != output->base.x || + ev->geometry.y != output->base.y || buffer == NULL || c->gbm == NULL || buffer->width != output->base.current_mode->width || buffer->height != output->base.current_mode->height || - output->base.transform != es->buffer_transform || - es->transform.enabled) + output->base.transform != ev->surface->buffer_viewport.transform || + ev->transform.enabled) return NULL; bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER, @@ -465,7 +474,7 @@ drm_output_prepare_scanout_surface(struct weston_output *_output, if (!bo) return NULL; - format = drm_output_check_scanout_format(output, es, bo); + format = drm_output_check_scanout_format(output, ev->surface, bo); if (format == 0) { gbm_bo_destroy(bo); return NULL; @@ -587,7 +596,8 @@ drm_output_repaint(struct weston_output *output_base, return -1; mode = container_of(output->base.current_mode, struct drm_mode, base); - if (!output->current) { + if (!output->current || + output->current->stride != output->next->stride) { ret = drmModeSetCrtc(compositor->drm.fd, output->crtc_id, output->next->fb_id, 0, 0, &output->connector_id, 1, @@ -757,7 +767,7 @@ page_flip_handler(int fd, unsigned int frame, static uint32_t drm_output_check_sprite_format(struct drm_sprite *s, - struct weston_surface *es, struct gbm_bo *bo) + struct weston_view *ev, struct gbm_bo *bo) { uint32_t i, format; @@ -767,9 +777,9 @@ drm_output_check_sprite_format(struct drm_sprite *s, pixman_region32_t r; pixman_region32_init_rect(&r, 0, 0, - es->geometry.width, - es->geometry.height); - pixman_region32_subtract(&r, &r, &es->opaque); + ev->surface->width, + ev->surface->height); + pixman_region32_subtract(&r, &r, &ev->surface->opaque); if (!pixman_region32_not_empty(&r)) format = GBM_FORMAT_XRGB8888; @@ -785,15 +795,15 @@ drm_output_check_sprite_format(struct drm_sprite *s, } static int -drm_surface_transform_supported(struct weston_surface *es) +drm_view_transform_supported(struct weston_view *ev) { - return !es->transform.enabled || - (es->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE); + return !ev->transform.enabled || + (ev->transform.matrix.type < WESTON_MATRIX_TRANSFORM_ROTATE); } static struct weston_plane * -drm_output_prepare_overlay_surface(struct weston_output *output_base, - struct weston_surface *es) +drm_output_prepare_overlay_view(struct weston_output *output_base, + struct weston_view *ev) { struct weston_compositor *ec = output_base->compositor; struct drm_compositor *c =(struct drm_compositor *) ec; @@ -808,28 +818,28 @@ drm_output_prepare_overlay_surface(struct weston_output *output_base, if (c->gbm == NULL) return NULL; - if (es->buffer_transform != output_base->transform) + if (ev->surface->buffer_viewport.transform != output_base->transform) return NULL; - if (es->buffer_scale != output_base->current_scale) + if (ev->surface->buffer_viewport.scale != output_base->current_scale) return NULL; if (c->sprites_are_broken) return NULL; - if (es->output_mask != (1u << output_base->id)) + if (ev->output_mask != (1u << output_base->id)) return NULL; - if (es->buffer_ref.buffer == NULL) + if (ev->surface->buffer_ref.buffer == NULL) return NULL; - if (es->alpha != 1.0f) + if (ev->alpha != 1.0f) return NULL; - if (wl_shm_buffer_get(es->buffer_ref.buffer->resource)) + if (wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource)) return NULL; - if (!drm_surface_transform_supported(es)) + if (!drm_view_transform_supported(ev)) return NULL; wl_list_for_each(s, &c->sprite_list, link) { @@ -847,12 +857,12 @@ drm_output_prepare_overlay_surface(struct weston_output *output_base, return NULL; bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER, - es->buffer_ref.buffer->resource, + ev->surface->buffer_ref.buffer->resource, GBM_BO_USE_SCANOUT); if (!bo) return NULL; - format = drm_output_check_sprite_format(s, es, bo); + format = drm_output_check_sprite_format(s, ev, bo); if (format == 0) { gbm_bo_destroy(bo); return NULL; @@ -864,9 +874,9 @@ drm_output_prepare_overlay_surface(struct weston_output *output_base, return NULL; } - drm_fb_set_buffer(s->next, es->buffer_ref.buffer); + drm_fb_set_buffer(s->next, ev->surface->buffer_ref.buffer); - box = pixman_region32_extents(&es->transform.boundingbox); + box = pixman_region32_extents(&ev->transform.boundingbox); s->plane.x = box->x1; s->plane.y = box->y1; @@ -876,7 +886,7 @@ drm_output_prepare_overlay_surface(struct weston_output *output_base, * for us already). */ pixman_region32_init(&dest_rect); - pixman_region32_intersect(&dest_rect, &es->transform.boundingbox, + pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox, &output_base->region); pixman_region32_translate(&dest_rect, -output_base->x, -output_base->y); box = pixman_region32_extents(&dest_rect); @@ -892,36 +902,38 @@ drm_output_prepare_overlay_surface(struct weston_output *output_base, pixman_region32_fini(&dest_rect); pixman_region32_init(&src_rect); - pixman_region32_intersect(&src_rect, &es->transform.boundingbox, + pixman_region32_intersect(&src_rect, &ev->transform.boundingbox, &output_base->region); box = pixman_region32_extents(&src_rect); - weston_surface_from_global_fixed(es, - wl_fixed_from_int(box->x1), - wl_fixed_from_int(box->y1), - &sx1, &sy1); - weston_surface_from_global_fixed(es, - wl_fixed_from_int(box->x2), - wl_fixed_from_int(box->y2), - &sx2, &sy2); + weston_view_from_global_fixed(ev, + wl_fixed_from_int(box->x1), + wl_fixed_from_int(box->y1), + &sx1, &sy1); + weston_view_from_global_fixed(ev, + wl_fixed_from_int(box->x2), + wl_fixed_from_int(box->y2), + &sx2, &sy2); if (sx1 < 0) sx1 = 0; if (sy1 < 0) sy1 = 0; - if (sx2 > wl_fixed_from_int(es->geometry.width)) - sx2 = wl_fixed_from_int(es->geometry.width); - if (sy2 > wl_fixed_from_int(es->geometry.height)) - sy2 = wl_fixed_from_int(es->geometry.height); + if (sx2 > wl_fixed_from_int(ev->surface->width)) + sx2 = wl_fixed_from_int(ev->surface->width); + if (sy2 > wl_fixed_from_int(ev->surface->height)) + sy2 = wl_fixed_from_int(ev->surface->height); tbox.x1 = sx1; tbox.y1 = sy1; tbox.x2 = sx2; tbox.y2 = sy2; - tbox = weston_transformed_rect(wl_fixed_from_int(es->geometry.width), - wl_fixed_from_int(es->geometry.height), - es->buffer_transform, es->buffer_scale, tbox); + tbox = weston_transformed_rect(wl_fixed_from_int(ev->surface->width), + wl_fixed_from_int(ev->surface->height), + ev->surface->buffer_viewport.transform, + ev->surface->buffer_viewport.scale, + tbox); s->src_x = tbox.x1 << 8; s->src_y = tbox.y1 << 8; @@ -933,8 +945,8 @@ drm_output_prepare_overlay_surface(struct weston_output *output_base, } static struct weston_plane * -drm_output_prepare_cursor_surface(struct weston_output *output_base, - struct weston_surface *es) +drm_output_prepare_cursor_view(struct weston_output *output_base, + struct weston_view *ev) { struct drm_compositor *c = (struct drm_compositor *) output_base->compositor; @@ -944,18 +956,18 @@ drm_output_prepare_cursor_surface(struct weston_output *output_base, return NULL; if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL) return NULL; - if (output->cursor_surface) + if (output->cursor_view) return NULL; - if (es->output_mask != (1u << output_base->id)) + if (ev->output_mask != (1u << output_base->id)) return NULL; if (c->cursors_are_broken) return NULL; - if (es->buffer_ref.buffer == NULL || - !wl_shm_buffer_get(es->buffer_ref.buffer->resource) || - es->geometry.width > 64 || es->geometry.height > 64) + if (ev->surface->buffer_ref.buffer == NULL || + !wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) || + ev->surface->width > 64 || ev->surface->height > 64) return NULL; - output->cursor_surface = es; + output->cursor_view = ev; return &output->cursor_plane; } @@ -963,7 +975,8 @@ drm_output_prepare_cursor_surface(struct weston_output *output_base, static void drm_output_set_cursor(struct drm_output *output) { - struct weston_surface *es = output->cursor_surface; + struct weston_view *ev = output->cursor_view; + struct weston_buffer *buffer; struct drm_compositor *c = (struct drm_compositor *) output->base.compositor; EGLint handle, stride; @@ -972,24 +985,28 @@ drm_output_set_cursor(struct drm_output *output) unsigned char *s; int i, x, y; - output->cursor_surface = NULL; - if (es == NULL) { + output->cursor_view = NULL; + if (ev == NULL) { drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0); return; } - if (es->buffer_ref.buffer && + buffer = ev->surface->buffer_ref.buffer; + + if (buffer && pixman_region32_not_empty(&output->cursor_plane.damage)) { pixman_region32_fini(&output->cursor_plane.damage); pixman_region32_init(&output->cursor_plane.damage); output->current_cursor ^= 1; bo = output->cursor_bo[output->current_cursor]; memset(buf, 0, sizeof buf); - stride = wl_shm_buffer_get_stride(es->buffer_ref.buffer->shm_buffer); - s = wl_shm_buffer_get_data(es->buffer_ref.buffer->shm_buffer); - for (i = 0; i < es->geometry.height; i++) + stride = wl_shm_buffer_get_stride(buffer->shm_buffer); + s = wl_shm_buffer_get_data(buffer->shm_buffer); + wl_shm_buffer_begin_access(buffer->shm_buffer); + for (i = 0; i < ev->surface->height; i++) memcpy(buf + i * 64, s + i * stride, - es->geometry.width * 4); + ev->surface->width * 4); + wl_shm_buffer_end_access(buffer->shm_buffer); if (gbm_bo_write(bo, buf, sizeof buf) < 0) weston_log("failed update cursor: %m\n"); @@ -1002,8 +1019,8 @@ drm_output_set_cursor(struct drm_output *output) } } - x = (es->geometry.x - output->base.x) * output->base.current_scale; - y = (es->geometry.y - output->base.y) * output->base.current_scale; + x = (ev->geometry.x - output->base.x) * output->base.current_scale; + y = (ev->geometry.y - output->base.y) * output->base.current_scale; if (output->cursor_plane.x != x || output->cursor_plane.y != y) { if (drmModeMoveCursor(c->drm.fd, output->crtc_id, x, y)) { weston_log("failed to move cursor: %m\n"); @@ -1020,7 +1037,7 @@ drm_assign_planes(struct weston_output *output) { struct drm_compositor *c = (struct drm_compositor *) output->compositor; - struct weston_surface *es, *next; + struct weston_view *ev, *next; pixman_region32_t overlap, surface_overlap; struct weston_plane *primary, *next_plane; @@ -1039,36 +1056,45 @@ drm_assign_planes(struct weston_output *output) */ pixman_region32_init(&overlap); primary = &c->base.primary_plane; - wl_list_for_each_safe(es, next, &c->base.surface_list, link) { - /* test whether this buffer can ever go into a plane: - * non-shm, or small enough to be a cursor + + wl_list_for_each_safe(ev, next, &c->base.view_list, link) { + struct weston_surface *es = ev->surface; + + /* Test whether this buffer can ever go into a plane: + * non-shm, or small enough to be a cursor. + * + * Also, keep a reference when using the pixman renderer. + * That makes it possible to do a seamless switch to the GL + * renderer and since the pixman renderer keeps a reference + * to the buffer anyway, there is no side effects. */ - if ((es->buffer_ref.buffer && - !wl_shm_buffer_get(es->buffer_ref.buffer->resource)) || - (es->geometry.width <= 64 && es->geometry.height <= 64)) + if (c->use_pixman || + (es->buffer_ref.buffer && + (!wl_shm_buffer_get(es->buffer_ref.buffer->resource) || + (ev->surface->width <= 64 && ev->surface->height <= 64)))) es->keep_buffer = 1; else es->keep_buffer = 0; pixman_region32_init(&surface_overlap); pixman_region32_intersect(&surface_overlap, &overlap, - &es->transform.boundingbox); + &ev->transform.boundingbox); next_plane = NULL; if (pixman_region32_not_empty(&surface_overlap)) next_plane = primary; if (next_plane == NULL) - next_plane = drm_output_prepare_cursor_surface(output, es); + next_plane = drm_output_prepare_cursor_view(output, ev); if (next_plane == NULL) - next_plane = drm_output_prepare_scanout_surface(output, es); + next_plane = drm_output_prepare_scanout_view(output, ev); if (next_plane == NULL) - next_plane = drm_output_prepare_overlay_surface(output, es); + next_plane = drm_output_prepare_overlay_view(output, ev); if (next_plane == NULL) next_plane = primary; - weston_surface_move_to_plane(es, next_plane); + weston_view_move_to_plane(ev, next_plane); if (next_plane == primary) pixman_region32_union(&overlap, &overlap, - &es->transform.boundingbox); + &ev->transform.boundingbox); pixman_region32_fini(&surface_overlap); } @@ -1112,7 +1138,7 @@ drm_output_destroy(struct weston_output *output_base) if (c->use_pixman) { drm_output_fini_pixman(output); } else { - gl_renderer_output_destroy(output_base); + gl_renderer->output_destroy(output_base); gbm_surface_destroy(output->surface); } @@ -1120,7 +1146,6 @@ drm_output_destroy(struct weston_output *output_base) weston_plane_release(&output->cursor_plane); weston_output_destroy(&output->base); - wl_list_remove(&output->base.link); free(output); } @@ -1203,7 +1228,7 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo return -1; } } else { - gl_renderer_output_destroy(&output->base); + gl_renderer->output_destroy(&output->base); gbm_surface_destroy(output->surface); if (drm_output_init_egl(output, ec) < 0) { @@ -1268,19 +1293,51 @@ init_drm(struct drm_compositor *ec, struct udev_device *device) return 0; } +static struct gbm_device * +create_gbm_device(int fd) +{ + struct gbm_device *gbm; + + gl_renderer = weston_load_module("gl-renderer.so", + "gl_renderer_interface"); + if (!gl_renderer) + return NULL; + + /* GBM will load a dri driver, but even though they need symbols from + * libglapi, in some version of Mesa they are not linked to it. Since + * only the gl-renderer module links to it, the call above won't make + * these symbols globally available, and loading the DRI driver fails. + * Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL. */ + dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL); + + gbm = gbm_create_device(fd); + + return gbm; +} + static int -init_egl(struct drm_compositor *ec) +drm_compositor_create_gl_renderer(struct drm_compositor *ec) { EGLint format; - ec->gbm = gbm_create_device(ec->drm.fd); + format = ec->format; + if (gl_renderer->create(&ec->base, ec->gbm, + gl_renderer->opaque_attribs, &format) < 0) { + return -1; + } + + return 0; +} + +static int +init_egl(struct drm_compositor *ec) +{ + ec->gbm = create_gbm_device(ec->drm.fd); if (!ec->gbm) return -1; - format = ec->format; - if (gl_renderer_create(&ec->base, ec->gbm, - gl_renderer_opaque_attribs, &format) < 0) { + if (drm_compositor_create_gl_renderer(ec) < 0) { gbm_device_destroy(ec->gbm); return -1; } @@ -1482,7 +1539,7 @@ drm_output_init_egl(struct drm_output *output, struct drm_compositor *ec) return -1; } - if (gl_renderer_output_create(&output->base, output->surface) < 0) { + if (gl_renderer->output_create(&output->base, output->surface) < 0) { weston_log("failed to create gl renderer output state\n"); gbm_surface_destroy(output->surface); return -1; @@ -1800,7 +1857,7 @@ create_output_for_connector(struct drm_compositor *ec, int x, int y, struct udev_device *drm_device) { struct drm_output *output; - struct drm_mode *drm_mode, *next, *preferred, *current, *configured; + struct drm_mode *drm_mode, *next, *preferred, *current, *configured, *best; struct weston_mode *m; struct weston_config_section *section; drmModeEncoder *encoder; @@ -1903,6 +1960,7 @@ create_output_for_connector(struct drm_compositor *ec, preferred = NULL; current = NULL; configured = NULL; + best = NULL; wl_list_for_each_reverse(drm_mode, &output->base.mode_list, base.link) { if (config == OUTPUT_CONFIG_MODE && @@ -1913,6 +1971,7 @@ create_output_for_connector(struct drm_compositor *ec, current = drm_mode; if (drm_mode->base.flags & WL_OUTPUT_MODE_PREFERRED) preferred = drm_mode; + best = drm_mode; } if (config == OUTPUT_CONFIG_MODELINE) { @@ -1938,6 +1997,8 @@ create_output_for_connector(struct drm_compositor *ec, output->base.current_mode = &preferred->base; else if (current) output->base.current_mode = ¤t->base; + else if (best) + output->base.current_mode = &best->base; if (output->base.current_mode == NULL) { weston_log("no available modes for %s\n", output->base.name); @@ -2050,7 +2111,7 @@ create_sprites(struct drm_compositor *ec) if (!sprite) { weston_log("%s: out of memory\n", __func__); - free(plane); + drmModeFreePlane(plane); continue; } @@ -2165,7 +2226,6 @@ update_outputs(struct drm_compositor *ec, struct udev_device *drm_device) drmModeRes *resources; struct drm_output *output, *next; int x = 0, y = 0; - int x_offset = 0, y_offset = 0; uint32_t connected = 0, disconnects = 0; int i; @@ -2215,17 +2275,10 @@ update_outputs(struct drm_compositor *ec, struct udev_device *drm_device) if (disconnects) { wl_list_for_each_safe(output, next, &ec->base.output_list, base.link) { - if (x_offset != 0 || y_offset != 0) { - weston_output_move(&output->base, - output->base.x - x_offset, - output->base.y - y_offset); - } - if (disconnects & (1 << output->connector_id)) { disconnects &= ~(1 << output->connector_id); weston_log("connector %d disconnected\n", output->connector_id); - x_offset += output->base.width; drm_output_destroy(&output->base); } } @@ -2289,8 +2342,6 @@ drm_destroy(struct weston_compositor *ec) weston_compositor_shutdown(ec); - ec->renderer->destroy(ec); - if (d->gbm) gbm_device_destroy(d->gbm); @@ -2343,7 +2394,6 @@ session_notify(struct wl_listener *listener, void *data) if (ec->base.session_active) { weston_log("activating session\n"); - compositor->focus = 1; compositor->state = ec->prev_state; drm_compositor_set_modes(ec); weston_compositor_damage_all(compositor); @@ -2352,7 +2402,6 @@ session_notify(struct wl_listener *listener, void *data) weston_log("deactivating session\n"); udev_input_disable(&ec->input); - compositor->focus = 0; ec->prev_state = compositor->state; weston_compositor_offscreen(compositor); @@ -2557,16 +2606,62 @@ recorder_binding(struct weston_seat *seat, uint32_t time, uint32_t key, } #endif +static void +switch_to_gl_renderer(struct drm_compositor *c) +{ + struct drm_output *output; + + if (!c->use_pixman) + return; + + weston_log("Switching to GL renderer\n"); + + c->gbm = create_gbm_device(c->drm.fd); + if (!c->gbm) { + weston_log("Failed to create gbm device. " + "Aborting renderer switch\n"); + return; + } + + wl_list_for_each(output, &c->base.output_list, base.link) + pixman_renderer_output_destroy(&output->base); + + c->base.renderer->destroy(&c->base); + + if (drm_compositor_create_gl_renderer(c) < 0) { + gbm_device_destroy(c->gbm); + weston_log("Failed to create GL renderer. Quitting.\n"); + /* FIXME: we need a function to shutdown cleanly */ + assert(0); + } + + wl_list_for_each(output, &c->base.output_list, base.link) + drm_output_init_egl(output, c); + + c->use_pixman = 0; +} + +static void +renderer_switch_binding(struct weston_seat *seat, uint32_t time, uint32_t key, + void *data) +{ + struct drm_compositor *c = (struct drm_compositor *) seat->compositor; + + switch_to_gl_renderer(c); +} + static struct weston_compositor * drm_compositor_create(struct wl_display *display, - int connector, const char *seat_id, int tty, int pixman, + struct drm_parameters *param, int *argc, char *argv[], struct weston_config *config) { struct drm_compositor *ec; + struct weston_config_section *section; struct udev_device *drm_device; struct wl_event_loop *loop; const char *path; + char *s; uint32_t key; weston_log("initializing drm backend\n"); @@ -2578,8 +2673,24 @@ drm_compositor_create(struct wl_display *display, /* KMS support for sprites is not complete yet, so disable the * functionality for now. */ ec->sprites_are_broken = 1; - ec->format = GBM_FORMAT_XRGB8888; - ec->use_pixman = pixman; + + section = weston_config_get_section(config, "core", NULL, NULL); + weston_config_section_get_string(section, + "gbm-format", &s, "xrgb8888"); + if (strcmp(s, "xrgb8888") == 0) + ec->format = GBM_FORMAT_XRGB8888; + else if (strcmp(s, "rgb565") == 0) + ec->format = GBM_FORMAT_RGB565; + else if (strcmp(s, "xrgb2101010") == 0) + ec->format = GBM_FORMAT_XRGB2101010; + else { + weston_log("fatal: unrecognized pixel format: %s\n", s); + free(s); + goto err_base; + } + free(s); + + ec->use_pixman = param->use_pixman; if (weston_compositor_init(&ec->base, display, argc, argv, config) < 0) { @@ -2588,7 +2699,8 @@ drm_compositor_create(struct wl_display *display, } /* Check if we run drm-backend using weston-launch */ - ec->base.launcher = weston_launcher_connect(&ec->base, tty); + ec->base.launcher = weston_launcher_connect(&ec->base, param->tty, + param->seat_id); if (ec->base.launcher == NULL) { weston_log("fatal: drm backend should be run " "using weston-launch binary or as root\n"); @@ -2605,7 +2717,7 @@ drm_compositor_create(struct wl_display *display, ec->session_listener.notify = session_notify; wl_signal_add(&ec->base.session_signal, &ec->session_listener); - drm_device = find_primary_gpu(ec, seat_id); + drm_device = find_primary_gpu(ec, param->seat_id); if (drm_device == NULL) { weston_log("no drm device found\n"); goto err_udev; @@ -2632,8 +2744,6 @@ drm_compositor_create(struct wl_display *display, ec->base.destroy = drm_destroy; ec->base.restore = drm_restore; - ec->base.focus = 1; - ec->prev_state = WESTON_COMPOSITOR_ACTIVE; for (key = KEY_F1; key < KEY_F9; key++) @@ -2644,14 +2754,15 @@ drm_compositor_create(struct wl_display *display, wl_list_init(&ec->sprite_list); create_sprites(ec); - if (create_outputs(ec, connector, drm_device) < 0) { + if (create_outputs(ec, param->connector, drm_device) < 0) { weston_log("failed to create output for %s\n", path); goto err_sprite; } path = NULL; - if (udev_input_init(&ec->input, &ec->base, ec->udev, seat_id) < 0) { + if (udev_input_init(&ec->input, + &ec->base, ec->udev, param->seat_id) < 0) { weston_log("failed to create input devices\n"); goto err_sprite; } @@ -2688,6 +2799,8 @@ drm_compositor_create(struct wl_display *display, planes_binding, ec); weston_compositor_add_debug_binding(&ec->base, KEY_Q, recorder_binding, ec); + weston_compositor_add_debug_binding(&ec->base, KEY_W, + renderer_switch_binding, ec); return &ec->base; @@ -2718,19 +2831,19 @@ WL_EXPORT struct weston_compositor * backend_init(struct wl_display *display, int *argc, char *argv[], struct weston_config *config) { - int connector = 0, tty = 0, use_pixman = 0; - const char *seat_id = default_seat; + struct drm_parameters param = { 0, }; const struct weston_option drm_options[] = { - { WESTON_OPTION_INTEGER, "connector", 0, &connector }, - { WESTON_OPTION_STRING, "seat", 0, &seat_id }, - { WESTON_OPTION_INTEGER, "tty", 0, &tty }, + { WESTON_OPTION_INTEGER, "connector", 0, ¶m.connector }, + { WESTON_OPTION_STRING, "seat", 0, ¶m.seat_id }, + { WESTON_OPTION_INTEGER, "tty", 0, ¶m.tty }, { WESTON_OPTION_BOOLEAN, "current-mode", 0, &option_current_mode }, - { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &use_pixman }, + { WESTON_OPTION_BOOLEAN, "use-pixman", 0, ¶m.use_pixman }, }; + param.seat_id = default_seat; + parse_options(drm_options, ARRAY_LENGTH(drm_options), argc, argv); - return drm_compositor_create(display, connector, seat_id, tty, use_pixman, - argc, argv, config); + return drm_compositor_create(display, ¶m, argc, argv, config); } diff --git a/src/compositor-fbdev.c b/src/compositor-fbdev.c index 24140eff..0d962693 100644 --- a/src/compositor-fbdev.c +++ b/src/compositor-fbdev.c @@ -95,6 +95,8 @@ struct fbdev_parameters { int use_gl; }; +struct gl_renderer_interface *gl_renderer; + static const char default_seat[] = "seat0"; static inline struct fbdev_output * @@ -625,7 +627,7 @@ fbdev_output_create(struct fbdev_compositor *compositor, goto out_shadow_surface; } else { setenv("HYBRIS_EGLPLATFORM", "wayland", 1); - if (gl_renderer_output_create(&output->base, + if (gl_renderer->output_create(&output->base, (EGLNativeWindowType)NULL) < 0) { weston_log("gl_renderer_output_create failed.\n"); goto out_shadow_surface; @@ -686,11 +688,10 @@ fbdev_output_destroy(struct weston_output *base) output->shadow_buf = NULL; } } else { - gl_renderer_output_destroy(base); + gl_renderer->output_destroy(base); } /* Remove the output. */ - wl_list_remove(&output->base.link); weston_output_destroy(&output->base); free(output); @@ -800,7 +801,6 @@ fbdev_compositor_destroy(struct weston_compositor *base) weston_compositor_shutdown(&compositor->base); /* Chain up. */ - compositor->base.renderer->destroy(&compositor->base); weston_launcher_destroy(compositor->base.launcher); free(compositor); @@ -814,7 +814,6 @@ session_notify(struct wl_listener *listener, void *data) if (compositor->base.session_active) { weston_log("entering VT\n"); - compositor->base.focus = 1; compositor->base.state = compositor->prev_state; wl_list_for_each(output, &compositor->base.output_list, link) { @@ -832,7 +831,6 @@ session_notify(struct wl_listener *listener, void *data) fbdev_output_disable(output); } - compositor->base.focus = 0; compositor->prev_state = compositor->base.state; weston_compositor_offscreen(&compositor->base); @@ -894,7 +892,7 @@ fbdev_compositor_create(struct wl_display *display, int *argc, char *argv[], wl_signal_add(&compositor->base.session_signal, &compositor->session_listener); compositor->base.launcher = - weston_launcher_connect(&compositor->base, param->tty); + weston_launcher_connect(&compositor->base, param->tty, "seat0"); if (!compositor->base.launcher) { weston_log("fatal: fbdev backend should be run " "using weston-launch binary or as root\n"); @@ -904,7 +902,6 @@ fbdev_compositor_create(struct wl_display *display, int *argc, char *argv[], compositor->base.destroy = fbdev_compositor_destroy; compositor->base.restore = fbdev_restore; - compositor->base.focus = 1; compositor->prev_state = WESTON_COMPOSITOR_ACTIVE; compositor->use_pixman = !param->use_gl; @@ -917,8 +914,16 @@ fbdev_compositor_create(struct wl_display *display, int *argc, char *argv[], if (pixman_renderer_init(&compositor->base) < 0) goto out_launcher; } else { - if (gl_renderer_create(&compositor->base, EGL_DEFAULT_DISPLAY, - gl_renderer_opaque_attribs, NULL) < 0) { + gl_renderer = weston_load_module("gl-renderer.so", + "gl_renderer_interface"); + if (!gl_renderer) { + weston_log("could not load gl renderer\n"); + goto out_launcher; + } + + if (gl_renderer->create(&compositor->base, EGL_DEFAULT_DISPLAY, + gl_renderer->opaque_attribs, + NULL) < 0) { weston_log("gl_renderer_create failed.\n"); goto out_launcher; } diff --git a/src/compositor-headless.c b/src/compositor-headless.c index 9d9f6dd3..5a5c1e68 100644 --- a/src/compositor-headless.c +++ b/src/compositor-headless.c @@ -114,8 +114,6 @@ headless_compositor_create_output(struct headless_compositor *c, output->base.make = "weston"; output->base.model = "headless"; - weston_output_move(&output->base, 0, 0); - loop = wl_display_get_event_loop(c->base.wl_display); output->finish_frame_timer = wl_event_loop_add_timer(loop, finish_frame_handler, output); @@ -143,8 +141,6 @@ headless_destroy(struct weston_compositor *ec) { struct headless_compositor *c = (struct headless_compositor *) ec; - ec->renderer->destroy(ec); - weston_seat_release(&c->fake_seat); weston_compositor_shutdown(ec); diff --git a/src/compositor-rdp.c b/src/compositor-rdp.c index 8a302f85..b9c36cfd 100644 --- a/src/compositor-rdp.c +++ b/src/compositor-rdp.c @@ -486,8 +486,6 @@ rdp_compositor_create_output(struct rdp_compositor *c, int width, int height, if (pixman_renderer_output_create(&output->base) < 0) goto out_shadow_surface; - weston_output_move(&output->base, 0, 0); - loop = wl_display_get_event_loop(c->base.wl_display); output->finish_frame_timer = wl_event_loop_add_timer(loop, finish_frame_handler, output); @@ -523,7 +521,6 @@ rdp_restore(struct weston_compositor *ec) static void rdp_destroy(struct weston_compositor *ec) { - ec->renderer->destroy(ec); weston_compositor_shutdown(ec); free(ec); @@ -599,8 +596,11 @@ rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context) wl_event_source_remove(context->events[i]); } - if(context->item.flags & RDP_PEER_ACTIVATED) + if(context->item.flags & RDP_PEER_ACTIVATED) { + weston_seat_release_keyboard(&context->item.seat); + weston_seat_release_pointer(&context->item.seat); weston_seat_release(&context->item.seat); + } Stream_Free(context->encode_stream, TRUE); nsc_context_free(context->nsc_context); rfx_context_free(context->rfx_context); diff --git a/src/compositor-rpi.c b/src/compositor-rpi.c index 60926325..399090dd 100644 --- a/src/compositor-rpi.c +++ b/src/compositor-rpi.c @@ -45,6 +45,7 @@ #include "rpi-renderer.h" #include "evdev.h" #include "launcher-util.h" +#include "udev-seat.h" #if 0 #define DBG(...) \ @@ -87,6 +88,7 @@ struct rpi_compositor { uint32_t prev_state; struct udev *udev; + struct udev_input input; struct wl_listener session_listener; int single_buffer; @@ -276,7 +278,6 @@ rpi_output_destroy(struct weston_output *base) */ rpi_flippipe_release(&output->flippipe); - wl_list_remove(&output->base.link); weston_output_destroy(&output->base); vc_dispmanx_display_close(output->display); @@ -414,248 +415,16 @@ out_free: return -1; } -static void -rpi_led_update(struct weston_seat *seat_base, enum weston_led leds) -{ - struct rpi_seat *seat = to_rpi_seat(seat_base); - struct evdev_device *device; - - wl_list_for_each(device, &seat->devices_list, link) - evdev_led_update(device, leds); -} - -static const char default_seat[] = "seat0"; - -static void -device_added(struct udev_device *udev_device, struct rpi_seat *master) -{ - struct evdev_device *device; - const char *devnode; - const char *device_seat; - int fd; - - device_seat = udev_device_get_property_value(udev_device, "ID_SEAT"); - if (!device_seat) - device_seat = default_seat; - - if (strcmp(device_seat, master->seat_id)) - return; - - devnode = udev_device_get_devnode(udev_device); - - /* Use non-blocking mode so that we can loop on read on - * evdev_device_data() until all events on the fd are - * read. mtdev_get() also expects this. */ - fd = open(devnode, O_RDWR | O_NONBLOCK | O_CLOEXEC); - if (fd < 0) { - weston_log("opening input device '%s' failed.\n", devnode); - return; - } - - device = evdev_device_create(&master->base, devnode, fd); - if (!device) { - close(fd); - weston_log("not using input device '%s'.\n", devnode); - return; - } - - wl_list_insert(master->devices_list.prev, &device->link); -} - -static void -evdev_add_devices(struct udev *udev, struct weston_seat *seat_base) -{ - struct rpi_seat *seat = to_rpi_seat(seat_base); - struct udev_enumerate *e; - struct udev_list_entry *entry; - struct udev_device *device; - const char *path, *sysname; - - e = udev_enumerate_new(udev); - udev_enumerate_add_match_subsystem(e, "input"); - udev_enumerate_scan_devices(e); - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { - path = udev_list_entry_get_name(entry); - device = udev_device_new_from_syspath(udev, path); - - sysname = udev_device_get_sysname(device); - if (strncmp("event", sysname, 5) != 0) { - udev_device_unref(device); - continue; - } - - device_added(device, seat); - - udev_device_unref(device); - } - udev_enumerate_unref(e); - - evdev_notify_keyboard_focus(&seat->base, &seat->devices_list); - - if (wl_list_empty(&seat->devices_list)) { - weston_log( - "warning: no input devices on entering Weston. " - "Possible causes:\n" - "\t- no permissions to read /dev/input/event*\n" - "\t- seats misconfigured " - "(Weston backend option 'seat', " - "udev device property ID_SEAT)\n"); - } -} - -static int -evdev_udev_handler(int fd, uint32_t mask, void *data) -{ - struct rpi_seat *seat = data; - struct udev_device *udev_device; - struct evdev_device *device, *next; - const char *action; - const char *devnode; - - udev_device = udev_monitor_receive_device(seat->udev_monitor); - if (!udev_device) - return 1; - - action = udev_device_get_action(udev_device); - if (!action) - goto out; - - if (strncmp("event", udev_device_get_sysname(udev_device), 5) != 0) - goto out; - - if (!strcmp(action, "add")) { - device_added(udev_device, seat); - } - else if (!strcmp(action, "remove")) { - devnode = udev_device_get_devnode(udev_device); - wl_list_for_each_safe(device, next, &seat->devices_list, link) - if (!strcmp(device->devnode, devnode)) { - weston_log("input device %s, %s removed\n", - device->devname, device->devnode); - evdev_device_destroy(device); - break; - } - } - -out: - udev_device_unref(udev_device); - - return 0; -} - -static int -evdev_enable_udev_monitor(struct udev *udev, struct weston_seat *seat_base) -{ - struct rpi_seat *master = to_rpi_seat(seat_base); - struct wl_event_loop *loop; - struct weston_compositor *c = master->base.compositor; - int fd; - - master->udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); - if (!master->udev_monitor) { - weston_log("udev: failed to create the udev monitor\n"); - return 0; - } - - udev_monitor_filter_add_match_subsystem_devtype(master->udev_monitor, - "input", NULL); - - if (udev_monitor_enable_receiving(master->udev_monitor)) { - weston_log("udev: failed to bind the udev monitor\n"); - udev_monitor_unref(master->udev_monitor); - return 0; - } - - loop = wl_display_get_event_loop(c->wl_display); - fd = udev_monitor_get_fd(master->udev_monitor); - master->udev_monitor_source = - wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, - evdev_udev_handler, master); - if (!master->udev_monitor_source) { - udev_monitor_unref(master->udev_monitor); - return 0; - } - - return 1; -} - -static void -evdev_disable_udev_monitor(struct weston_seat *seat_base) -{ - struct rpi_seat *seat = to_rpi_seat(seat_base); - - if (!seat->udev_monitor) - return; - - udev_monitor_unref(seat->udev_monitor); - seat->udev_monitor = NULL; - wl_event_source_remove(seat->udev_monitor_source); - seat->udev_monitor_source = NULL; -} - -static void -evdev_input_create(struct weston_compositor *c, struct udev *udev, - const char *seat_id) -{ - struct rpi_seat *seat; - - seat = zalloc(sizeof *seat); - if (seat == NULL) - return; - - weston_seat_init(&seat->base, c, "default"); - seat->base.led_update = rpi_led_update; - - wl_list_init(&seat->devices_list); - seat->seat_id = strdup(seat_id); - if (!evdev_enable_udev_monitor(udev, &seat->base)) { - free(seat->seat_id); - free(seat); - return; - } - - evdev_add_devices(udev, &seat->base); -} - -static void -evdev_remove_devices(struct weston_seat *seat_base) -{ - struct rpi_seat *seat = to_rpi_seat(seat_base); - struct evdev_device *device, *next; - - wl_list_for_each_safe(device, next, &seat->devices_list, link) - evdev_device_destroy(device); - - if (seat->base.keyboard) - notify_keyboard_focus_out(&seat->base); -} - -static void -evdev_input_destroy(struct weston_seat *seat_base) -{ - struct rpi_seat *seat = to_rpi_seat(seat_base); - - evdev_remove_devices(seat_base); - evdev_disable_udev_monitor(&seat->base); - - weston_seat_release(seat_base); - free(seat->seat_id); - free(seat); -} - static void rpi_compositor_destroy(struct weston_compositor *base) { struct rpi_compositor *compositor = to_rpi_compositor(base); - struct weston_seat *seat, *next; - wl_list_for_each_safe(seat, next, &compositor->base.seat_list, link) - evdev_input_destroy(seat); + udev_input_destroy(&compositor->input); /* destroys outputs, too */ weston_compositor_shutdown(&compositor->base); - compositor->base.renderer->destroy(&compositor->base); weston_launcher_destroy(compositor->base.launcher); bcm_host_deinit(); @@ -666,26 +435,17 @@ static void session_notify(struct wl_listener *listener, void *data) { struct rpi_compositor *compositor = data; - struct weston_seat *seat; struct weston_output *output; if (compositor->base.session_active) { weston_log("activating session\n"); - compositor->base.focus = 1; compositor->base.state = compositor->prev_state; weston_compositor_damage_all(&compositor->base); - wl_list_for_each(seat, &compositor->base.seat_list, link) { - evdev_add_devices(compositor->udev, seat); - evdev_enable_udev_monitor(compositor->udev, seat); - } + udev_input_enable(&compositor->input, compositor->udev); } else { weston_log("deactivating session\n"); - wl_list_for_each(seat, &compositor->base.seat_list, link) { - evdev_disable_udev_monitor(seat); - evdev_remove_devices(seat); - } + udev_input_disable(&compositor->input); - compositor->base.focus = 0; compositor->prev_state = compositor->base.state; weston_compositor_offscreen(&compositor->base); @@ -730,7 +490,6 @@ rpi_compositor_create(struct wl_display *display, int *argc, char *argv[], struct rpi_parameters *param) { struct rpi_compositor *compositor; - const char *seat = default_seat; uint32_t key; weston_log("initializing Raspberry Pi backend\n"); @@ -753,7 +512,7 @@ rpi_compositor_create(struct wl_display *display, int *argc, char *argv[], wl_signal_add(&compositor->base.session_signal, &compositor ->session_listener); compositor->base.launcher = - weston_launcher_connect(&compositor->base, param->tty); + weston_launcher_connect(&compositor->base, param->tty, "seat0"); if (!compositor->base.launcher) { weston_log("Failed to initialize tty.\n"); goto out_udev; @@ -762,13 +521,19 @@ rpi_compositor_create(struct wl_display *display, int *argc, char *argv[], compositor->base.destroy = rpi_compositor_destroy; compositor->base.restore = rpi_restore; - compositor->base.focus = 1; compositor->prev_state = WESTON_COMPOSITOR_ACTIVE; compositor->single_buffer = param->renderer.single_buffer; weston_log("Dispmanx planes are %s buffered.\n", compositor->single_buffer ? "single" : "double"); + if (udev_input_init(&compositor->input, + &compositor->base, + compositor->udev, "seat0") != 0) { + weston_log("Failed to initialize udev input.\n"); + goto out_launcher; + } + for (key = KEY_F1; key < KEY_F9; key++) weston_compositor_add_key_binding(&compositor->base, key, MODIFIER_CTRL | MODIFIER_ALT, @@ -784,18 +549,19 @@ rpi_compositor_create(struct wl_display *display, int *argc, char *argv[], bcm_host_init(); if (rpi_renderer_create(&compositor->base, ¶m->renderer) < 0) - goto out_launcher; + goto out_udev_input; if (rpi_output_create(compositor, param->output_transform) < 0) goto out_renderer; - evdev_input_create(&compositor->base, compositor->udev, seat); - return &compositor->base; out_renderer: compositor->base.renderer->destroy(&compositor->base); +out_udev_input: + udev_input_destroy(&compositor->input); + out_launcher: weston_launcher_destroy(compositor->base.launcher); @@ -823,6 +589,7 @@ backend_init(struct wl_display *display, int *argc, char *argv[], .tty = 0, /* default to current tty */ .renderer.single_buffer = 0, .output_transform = WL_OUTPUT_TRANSFORM_NORMAL, + .renderer.opaque_regions = 0, }; const struct weston_option rpi_options[] = { @@ -830,6 +597,8 @@ backend_init(struct wl_display *display, int *argc, char *argv[], { WESTON_OPTION_BOOLEAN, "single-buffer", 0, ¶m.renderer.single_buffer }, { WESTON_OPTION_STRING, "transform", 0, &transform }, + { WESTON_OPTION_BOOLEAN, "opaque-regions", 0, + ¶m.renderer.opaque_regions }, }; parse_options(rpi_options, ARRAY_LENGTH(rpi_options), argc, argv); diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c index f3b03619..d2d89427 100644 --- a/src/compositor-wayland.c +++ b/src/compositor-wayland.c @@ -1,5 +1,6 @@ /* * Copyright © 2010-2011 Benjamin Franzke + * Copyright © 2013 Jason Ekstrand * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -29,169 +30,347 @@ #include #include #include +#include #include #include +#include #include "compositor.h" #include "gl-renderer.h" +#include "pixman-renderer.h" #include "../shared/image-loader.h" #include "../shared/os-compatibility.h" +#include "../shared/cairo-util.h" + +#define WINDOW_TITLE "Weston Compositor" struct wayland_compositor { - struct weston_compositor base; + struct weston_compositor base; struct { struct wl_display *wl_display; struct wl_registry *registry; struct wl_compositor *compositor; struct wl_shell *shell; - struct wl_output *output; struct wl_shm *shm; - struct { - int32_t x, y, width, height; - } screen_allocation; - struct wl_event_source *wl_source; uint32_t event_mask; } parent; - struct { - int32_t top, bottom, left, right; - } border; + int use_pixman; + + struct theme *theme; + cairo_device_t *frame_device; + struct wl_cursor_theme *cursor_theme; + struct wl_cursor *cursor; struct wl_list input_list; }; struct wayland_output { - struct weston_output base; + struct weston_output base; + struct { - int draw_initial_frame; - struct wl_surface *surface; - struct wl_shell_surface *shell_surface; - struct wl_egl_window *egl_window; + int draw_initial_frame; + struct wl_surface *surface; + struct wl_shell_surface *shell_surface; + int configure_width, configure_height; } parent; - struct weston_mode mode; + + int keyboard_count; + + char *name; + struct frame *frame; + + struct { + struct wl_egl_window *egl_window; + struct { + cairo_surface_t *top; + cairo_surface_t *left; + cairo_surface_t *right; + cairo_surface_t *bottom; + } border; + } gl; + + struct { + struct wl_list buffers; + struct wl_list free_buffers; + } shm; + + struct weston_mode mode; + uint32_t scale; +}; + +struct wayland_shm_buffer { + struct wayland_output *output; + struct wl_list link; + struct wl_list free_link; + + struct wl_buffer *buffer; + void *data; + size_t size; + pixman_region32_t damage; + int frame_damaged; + + pixman_image_t *pm_image; + cairo_surface_t *c_surface; }; struct wayland_input { struct weston_seat base; struct wayland_compositor *compositor; - struct wl_seat *seat; - struct wl_pointer *pointer; - struct wl_keyboard *keyboard; - struct wl_touch *touch; struct wl_list link; + + struct { + struct wl_seat *seat; + struct wl_pointer *pointer; + struct wl_keyboard *keyboard; + struct wl_touch *touch; + + struct { + struct wl_surface *surface; + int32_t hx, hy; + } cursor; + } parent; + uint32_t key_serial; uint32_t enter_serial; int focus; struct wayland_output *output; + struct wayland_output *keyboard_focus; }; +struct gl_renderer_interface *gl_renderer; + static void -create_border(struct wayland_compositor *c) +wayland_shm_buffer_destroy(struct wayland_shm_buffer *buffer) { - pixman_image_t *image; - int32_t edges[4]; - - image = load_image(DATADIR "/weston/border.png"); - if (!image) { - weston_log("couldn't load border image\n"); - return; - } - - edges[0] = c->border.left; - edges[1] = c->border.right; - edges[2] = c->border.top; - edges[3] = c->border.bottom; - - gl_renderer_set_border(&c->base, pixman_image_get_width(image), - pixman_image_get_height(image), - pixman_image_get_data(image), edges); + cairo_surface_destroy(buffer->c_surface); + pixman_image_unref(buffer->pm_image); - pixman_image_unref(image); -} + wl_buffer_destroy(buffer->buffer); + munmap(buffer->data, buffer->size); -static void -frame_done(void *data, struct wl_callback *callback, uint32_t time) -{ - struct weston_output *output = data; + pixman_region32_fini(&buffer->damage); - wl_callback_destroy(callback); - weston_output_finish_frame(output, time); + wl_list_remove(&buffer->link); + wl_list_remove(&buffer->free_link); + free(buffer); } -static const struct wl_callback_listener frame_listener = { - frame_done -}; - static void buffer_release(void *data, struct wl_buffer *buffer) { - wl_buffer_destroy(buffer); + struct wayland_shm_buffer *sb = data; + + if (sb->output) { + wl_list_insert(&sb->output->shm.free_buffers, &sb->free_link); + } else { + wayland_shm_buffer_destroy(sb); + } } static const struct wl_buffer_listener buffer_listener = { buffer_release }; -static void -draw_initial_frame(struct wayland_output *output) +static struct wayland_shm_buffer * +wayland_output_get_shm_buffer(struct wayland_output *output) { struct wayland_compositor *c = (struct wayland_compositor *) output->base.compositor; struct wl_shm *shm = c->parent.shm; - struct wl_surface *surface = output->parent.surface; - struct wl_shm_pool *pool; - struct wl_buffer *buffer; + struct wayland_shm_buffer *sb; + struct wl_shm_pool *pool; int width, height, stride; - int size; + int32_t fx, fy; int fd; - void *data; + unsigned char *data; + + if (!wl_list_empty(&output->shm.free_buffers)) { + sb = container_of(output->shm.free_buffers.next, + struct wayland_shm_buffer, free_link); + wl_list_remove(&sb->free_link); + wl_list_init(&sb->free_link); + + return sb; + } + + if (output->frame) { + width = frame_width(output->frame); + height = frame_height(output->frame); + } else { + width = output->base.current_mode->width; + height = output->base.current_mode->height; + } - width = output->mode.width + c->border.left + c->border.right; - height = output->mode.height + c->border.top + c->border.bottom; - stride = width * 4; - size = height * stride; + stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - fd = os_create_anonymous_file(size); + fd = os_create_anonymous_file(height * stride); if (fd < 0) { perror("os_create_anonymous_file"); - return; + return NULL; } - data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + data = mmap(NULL, height * stride, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { perror("mmap"); close(fd); - return; + return NULL; } - pool = wl_shm_create_pool(shm, fd, size); + sb = zalloc(sizeof *sb); + + sb->output = output; + wl_list_init(&sb->free_link); + wl_list_insert(&output->shm.buffers, &sb->link); + + pixman_region32_init_rect(&sb->damage, 0, 0, + output->base.width, output->base.height); + sb->frame_damaged = 1; + + sb->data = data; + sb->size = height * stride; + + pool = wl_shm_create_pool(shm, fd, sb->size); - buffer = wl_shm_pool_create_buffer(pool, 0, - width, height, - stride, - WL_SHM_FORMAT_ARGB8888); - wl_buffer_add_listener(buffer, &buffer_listener, buffer); + sb->buffer = wl_shm_pool_create_buffer(pool, 0, + width, height, + stride, + WL_SHM_FORMAT_ARGB8888); + wl_buffer_add_listener(sb->buffer, &buffer_listener, sb); wl_shm_pool_destroy(pool); close(fd); - memset(data, 0, size); + memset(data, 0, sb->size); + + sb->c_surface = + cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, + width, height, stride); + + fx = 0; + fy = 0; + if (output->frame) + frame_interior(output->frame, &fx, &fy, 0, 0); + sb->pm_image = + pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, + (uint32_t *)(data + fy * stride) + fx, + stride); + + return sb; +} + +static void +frame_done(void *data, struct wl_callback *callback, uint32_t time) +{ + struct weston_output *output = data; + + wl_callback_destroy(callback); + weston_output_finish_frame(output, time); +} + +static const struct wl_callback_listener frame_listener = { + frame_done +}; + +static void +draw_initial_frame(struct wayland_output *output) +{ + struct wayland_shm_buffer *sb; + + sb = wayland_output_get_shm_buffer(output); - wl_surface_attach(surface, buffer, 0, 0); + /* If we are rendering with GL, then orphan it so that it gets + * destroyed immediately */ + if (output->gl.egl_window) + sb->output = NULL; + + wl_surface_attach(output->parent.surface, sb->buffer, 0, 0); /* We only need to damage some part, as its only transparant * pixels anyway. */ - wl_surface_damage(surface, 0, 0, 1, 1); + wl_surface_damage(output->parent.surface, 0, 0, 1, 1); +} + +static void +wayland_output_update_gl_border(struct wayland_output *output) +{ + int32_t ix, iy, iwidth, iheight, fwidth, fheight; + cairo_t *cr; + + if (!output->frame) + return; + if (!(frame_status(output->frame) & FRAME_STATUS_REPAINT)) + return; + + fwidth = frame_width(output->frame); + fheight = frame_height(output->frame); + frame_interior(output->frame, &ix, &iy, &iwidth, &iheight); + + if (!output->gl.border.top) + output->gl.border.top = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + fwidth, iy); + cr = cairo_create(output->gl.border.top); + frame_repaint(output->frame, cr); + cairo_destroy(cr); + gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_TOP, + fwidth, iy, + cairo_image_surface_get_stride(output->gl.border.top) / 4, + cairo_image_surface_get_data(output->gl.border.top)); + + + if (!output->gl.border.left) + output->gl.border.left = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + ix, 1); + cr = cairo_create(output->gl.border.left); + cairo_translate(cr, 0, -iy); + frame_repaint(output->frame, cr); + cairo_destroy(cr); + gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_LEFT, + ix, 1, + cairo_image_surface_get_stride(output->gl.border.left) / 4, + cairo_image_surface_get_data(output->gl.border.left)); + + + if (!output->gl.border.right) + output->gl.border.right = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + fwidth - (ix + iwidth), 1); + cr = cairo_create(output->gl.border.right); + cairo_translate(cr, -(iwidth + ix), -iy); + frame_repaint(output->frame, cr); + cairo_destroy(cr); + gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_RIGHT, + fwidth - (ix + iwidth), 1, + cairo_image_surface_get_stride(output->gl.border.right) / 4, + cairo_image_surface_get_data(output->gl.border.right)); + + + if (!output->gl.border.bottom) + output->gl.border.bottom = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + fwidth, fheight - (iy + iheight)); + cr = cairo_create(output->gl.border.bottom); + cairo_translate(cr, 0, -(iy + iheight)); + frame_repaint(output->frame, cr); + cairo_destroy(cr); + gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_BOTTOM, + fwidth, fheight - (iy + iheight), + cairo_image_surface_get_stride(output->gl.border.bottom) / 4, + cairo_image_surface_get_data(output->gl.border.bottom)); } static void wayland_output_start_repaint_loop(struct weston_output *output_base) { struct wayland_output *output = (struct wayland_output *) output_base; + struct wayland_compositor *wc = + (struct wayland_compositor *)output->base.compositor; struct wl_callback *callback; /* If this is the initial frame, we need to attach a buffer so that @@ -208,11 +387,12 @@ wayland_output_start_repaint_loop(struct weston_output *output_base) callback = wl_surface_frame(output->parent.surface); wl_callback_add_listener(callback, &frame_listener, output); wl_surface_commit(output->parent.surface); + wl_display_flush(wc->parent.wl_display); } static int -wayland_output_repaint(struct weston_output *output_base, - pixman_region32_t *damage) +wayland_output_repaint_gl(struct weston_output *output_base, + pixman_region32_t *damage) { struct wayland_output *output = (struct wayland_output *) output_base; struct weston_compositor *ec = output->base.compositor; @@ -221,6 +401,8 @@ wayland_output_repaint(struct weston_output *output_base, callback = wl_surface_frame(output->parent.surface); wl_callback_add_listener(callback, &frame_listener, output); + wayland_output_update_gl_border(output); + ec->renderer->repaint_output(&output->base, damage); pixman_region32_subtract(&ec->primary_plane.damage, @@ -228,14 +410,160 @@ wayland_output_repaint(struct weston_output *output_base, return 0; } +static void +wayland_output_update_shm_border(struct wayland_shm_buffer *buffer) +{ + int32_t ix, iy, iwidth, iheight, fwidth, fheight; + cairo_t *cr; + + if (!buffer->output->frame || !buffer->frame_damaged) + return; + + cr = cairo_create(buffer->c_surface); + + frame_interior(buffer->output->frame, &ix, &iy, &iwidth, &iheight); + fwidth = frame_width(buffer->output->frame); + fheight = frame_height(buffer->output->frame); + + /* Set the clip so we don't unnecisaraly damage the surface */ + cairo_move_to(cr, ix, iy); + cairo_rel_line_to(cr, iwidth, 0); + cairo_rel_line_to(cr, 0, iheight); + cairo_rel_line_to(cr, -iwidth, 0); + cairo_line_to(cr, ix, iy); + cairo_line_to(cr, 0, iy); + cairo_line_to(cr, 0, fheight); + cairo_line_to(cr, fwidth, fheight); + cairo_line_to(cr, fwidth, 0); + cairo_line_to(cr, 0, 0); + cairo_line_to(cr, 0, iy); + cairo_close_path(cr); + cairo_clip(cr); + + /* Draw using a pattern so that the final result gets clipped */ + cairo_push_group(cr); + frame_repaint(buffer->output->frame, cr); + cairo_pop_group_to_source(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + + cairo_destroy(cr); +} + +static void +wayland_shm_buffer_attach(struct wayland_shm_buffer *sb) +{ + pixman_region32_t damage; + pixman_box32_t *rects; + int32_t ix, iy, iwidth, iheight, fwidth, fheight; + int i, n; + + pixman_region32_init(&damage); + weston_transformed_region(sb->output->base.width, + sb->output->base.height, + sb->output->base.transform, + sb->output->base.current_scale, + &sb->damage, &damage); + + if (sb->output->frame) { + frame_interior(sb->output->frame, &ix, &iy, &iwidth, &iheight); + fwidth = frame_width(sb->output->frame); + fheight = frame_height(sb->output->frame); + + pixman_region32_translate(&damage, ix, iy); + + if (sb->frame_damaged) { + pixman_region32_union_rect(&damage, &damage, + 0, 0, fwidth, iy); + pixman_region32_union_rect(&damage, &damage, + 0, iy, ix, iheight); + pixman_region32_union_rect(&damage, &damage, + ix + iwidth, iy, + fwidth - (ix + iwidth), iheight); + pixman_region32_union_rect(&damage, &damage, + 0, iy + iheight, + fwidth, fheight - (iy + iheight)); + } + } + + rects = pixman_region32_rectangles(&damage, &n); + wl_surface_attach(sb->output->parent.surface, sb->buffer, 0, 0); + for (i = 0; i < n; ++i) + wl_surface_damage(sb->output->parent.surface, rects[i].x1, + rects[i].y1, rects[i].x2 - rects[i].x1, + rects[i].y2 - rects[i].y1); + + if (sb->output->frame) + pixman_region32_fini(&damage); +} + +static int +wayland_output_repaint_pixman(struct weston_output *output_base, + pixman_region32_t *damage) +{ + struct wayland_output *output = (struct wayland_output *) output_base; + struct wayland_compositor *c = + (struct wayland_compositor *)output->base.compositor; + struct wl_callback *callback; + struct wayland_shm_buffer *sb; + + if (output->frame) { + if (frame_status(output->frame) & FRAME_STATUS_REPAINT) + wl_list_for_each(sb, &output->shm.buffers, link) + sb->frame_damaged = 1; + } + + wl_list_for_each(sb, &output->shm.buffers, link) + pixman_region32_union(&sb->damage, &sb->damage, damage); + + sb = wayland_output_get_shm_buffer(output); + + wayland_output_update_shm_border(sb); + pixman_renderer_output_set_buffer(output_base, sb->pm_image); + c->base.renderer->repaint_output(output_base, &sb->damage); + + wayland_shm_buffer_attach(sb); + + callback = wl_surface_frame(output->parent.surface); + wl_callback_add_listener(callback, &frame_listener, output); + wl_surface_commit(output->parent.surface); + wl_display_flush(c->parent.wl_display); + + pixman_region32_fini(&sb->damage); + pixman_region32_init(&sb->damage); + sb->frame_damaged = 0; + + pixman_region32_subtract(&c->base.primary_plane.damage, + &c->base.primary_plane.damage, damage); + return 0; +} + static void wayland_output_destroy(struct weston_output *output_base) { struct wayland_output *output = (struct wayland_output *) output_base; + struct wayland_compositor *c = + (struct wayland_compositor *) output->base.compositor; + + if (c->use_pixman) { + pixman_renderer_output_destroy(output_base); + } else { + gl_renderer->output_destroy(output_base); + } - gl_renderer_output_destroy(output_base); + wl_egl_window_destroy(output->gl.egl_window); + wl_surface_destroy(output->parent.surface); + wl_shell_surface_destroy(output->parent.shell_surface); - wl_egl_window_destroy(output->parent.egl_window); + if (output->frame) + frame_destroy(output->frame); + + cairo_surface_destroy(output->gl.border.top); + cairo_surface_destroy(output->gl.border.left); + cairo_surface_destroy(output->gl.border.right); + cairo_surface_destroy(output->gl.border.bottom); + + weston_output_destroy(&output->base); free(output); return; @@ -244,59 +572,255 @@ wayland_output_destroy(struct weston_output *output_base) static const struct wl_shell_surface_listener shell_surface_listener; static int -wayland_compositor_create_output(struct wayland_compositor *c, - int width, int height) +wayland_output_init_gl_renderer(struct wayland_output *output) { - struct wayland_output *output; + int32_t fwidth = 0, fheight = 0; + + if (output->frame) { + fwidth = frame_width(output->frame); + fheight = frame_height(output->frame); + } else { + fwidth = output->base.current_mode->width; + fheight = output->base.current_mode->height; + } - output = zalloc(sizeof *output); - if (output == NULL) + output->gl.egl_window = + wl_egl_window_create(output->parent.surface, + fwidth, fheight); + if (!output->gl.egl_window) { + weston_log("failure to create wl_egl_window\n"); return -1; + } - output->mode.flags = - WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; - output->mode.width = width; - output->mode.height = height; - output->mode.refresh = 60; - wl_list_init(&output->base.mode_list); - wl_list_insert(&output->base.mode_list, &output->mode.link); + if (gl_renderer->output_create(&output->base, + output->gl.egl_window) < 0) + goto cleanup_window; - output->base.current_mode = &output->mode; - weston_output_init(&output->base, &c->base, 0, 0, width, height, - WL_OUTPUT_TRANSFORM_NORMAL, 1); + return 0; + +cleanup_window: + wl_egl_window_destroy(output->gl.egl_window); + return -1; +} + +static int +wayland_output_init_pixman_renderer(struct wayland_output *output) +{ + return pixman_renderer_output_create(&output->base); +} + +static void +wayland_output_resize_surface(struct wayland_output *output) +{ + struct wayland_compositor *c = + (struct wayland_compositor *)output->base.compositor; + struct wayland_shm_buffer *buffer, *next; + int32_t ix, iy, iwidth, iheight; + int32_t width, height; + struct wl_region *region; + + width = output->base.current_mode->width; + height = output->base.current_mode->height; + + if (output->frame) { + frame_resize_inside(output->frame, width, height); + + frame_input_rect(output->frame, &ix, &iy, &iwidth, &iheight); + region = wl_compositor_create_region(c->parent.compositor); + wl_region_add(region, ix, iy, iwidth, iheight); + wl_surface_set_input_region(output->parent.surface, region); + wl_region_destroy(region); + + frame_opaque_rect(output->frame, &ix, &iy, &iwidth, &iheight); + region = wl_compositor_create_region(c->parent.compositor); + wl_region_add(region, ix, iy, iwidth, iheight); + wl_surface_set_opaque_region(output->parent.surface, region); + wl_region_destroy(region); + + width = frame_width(output->frame); + height = frame_height(output->frame); + } else { + region = wl_compositor_create_region(c->parent.compositor); + wl_region_add(region, 0, 0, width, height); + wl_surface_set_input_region(output->parent.surface, region); + wl_region_destroy(region); + + region = wl_compositor_create_region(c->parent.compositor); + wl_region_add(region, 0, 0, width, height); + wl_surface_set_opaque_region(output->parent.surface, region); + wl_region_destroy(region); + } + + if (output->gl.egl_window) { + wl_egl_window_resize(output->gl.egl_window, + width, height, 0, 0); + + /* These will need to be re-created due to the resize */ + gl_renderer->output_set_border(&output->base, + GL_RENDERER_BORDER_TOP, + 0, 0, 0, NULL); + cairo_surface_destroy(output->gl.border.top); + output->gl.border.top = NULL; + gl_renderer->output_set_border(&output->base, + GL_RENDERER_BORDER_LEFT, + 0, 0, 0, NULL); + cairo_surface_destroy(output->gl.border.left); + output->gl.border.left = NULL; + gl_renderer->output_set_border(&output->base, + GL_RENDERER_BORDER_RIGHT, + 0, 0, 0, NULL); + cairo_surface_destroy(output->gl.border.right); + output->gl.border.right = NULL; + gl_renderer->output_set_border(&output->base, + GL_RENDERER_BORDER_BOTTOM, + 0, 0, 0, NULL); + cairo_surface_destroy(output->gl.border.bottom); + output->gl.border.bottom = NULL; + } + + /* Throw away any remaining SHM buffers */ + wl_list_for_each_safe(buffer, next, &output->shm.free_buffers, link) + wayland_shm_buffer_destroy(buffer); + /* These will get thrown away when they get released */ + wl_list_for_each(buffer, &output->shm.buffers, link) + buffer->output = NULL; +} + +static int +wayland_output_set_windowed(struct wayland_output *output) +{ + struct wayland_compositor *c = + (struct wayland_compositor *)output->base.compositor; + int tlen; + char *title; + + if (output->frame) + return 0; + + if (output->name) { + tlen = strlen(output->name) + strlen(WINDOW_TITLE " - "); + title = malloc(tlen + 1); + if (!title) + return -1; + + snprintf(title, tlen + 1, WINDOW_TITLE " - %s", output->name); + } else { + title = strdup(WINDOW_TITLE); + } + + if (!c->theme) { + c->theme = theme_create(); + if (!c->theme) { + free(title); + return -1; + } + } + output->frame = frame_create(c->theme, 100, 100, + FRAME_BUTTON_CLOSE, title); + free(title); + if (!output->frame) + return -1; + if (output->keyboard_count) + frame_set_flag(output->frame, FRAME_FLAG_ACTIVE); + + wayland_output_resize_surface(output); + + wl_shell_surface_set_toplevel(output->parent.shell_surface); + + return 0; +} + +static void +wayland_output_set_fullscreen(struct wayland_output *output, + enum wl_shell_surface_fullscreen_method method, + uint32_t framerate, struct wl_output *target) +{ + if (output->frame) { + frame_destroy(output->frame); + output->frame = NULL; + } + + wayland_output_resize_surface(output); + + wl_shell_surface_set_fullscreen(output->parent.shell_surface, + method, framerate, target); +} + +static struct wayland_output * +wayland_output_create(struct wayland_compositor *c, int x, int y, + int width, int height, const char *name, int fullscreen, + uint32_t transform, int32_t scale) +{ + struct wayland_output *output; + int output_width, output_height; + + weston_log("Creating %dx%d wayland output at (%d, %d)\n", + width, height, x, y); + + output = zalloc(sizeof *output); + if (output == NULL) + return NULL; + + output->name = name ? strdup(name) : NULL; output->base.make = "waywayland"; output->base.model = "none"; - weston_output_move(&output->base, 0, 0); + output_width = width * scale; + output_height = height * scale; output->parent.surface = wl_compositor_create_surface(c->parent.compositor); + if (!output->parent.surface) + goto err_name; wl_surface_set_user_data(output->parent.surface, output); - output->parent.egl_window = - wl_egl_window_create(output->parent.surface, - width + c->border.left + c->border.right, - height + c->border.top + c->border.bottom); - if (!output->parent.egl_window) { - weston_log("failure to create wl_egl_window\n"); - goto cleanup_output; - } - - if (gl_renderer_output_create(&output->base, - output->parent.egl_window) < 0) - goto cleanup_window; - output->parent.draw_initial_frame = 1; output->parent.shell_surface = wl_shell_get_shell_surface(c->parent.shell, output->parent.surface); + if (!output->parent.shell_surface) + goto err_surface; wl_shell_surface_add_listener(output->parent.shell_surface, &shell_surface_listener, output); - wl_shell_surface_set_toplevel(output->parent.shell_surface); + + if (fullscreen) { + wl_shell_surface_set_fullscreen(output->parent.shell_surface, + 0, 0, NULL); + wl_display_roundtrip(c->parent.wl_display); + if (!width) + output_width = output->parent.configure_width; + if (!height) + output_height = output->parent.configure_height; + } + + output->mode.flags = + WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; + output->mode.width = output_width; + output->mode.height = output_height; + output->mode.refresh = 60000; + output->scale = scale; + wl_list_init(&output->base.mode_list); + wl_list_insert(&output->base.mode_list, &output->mode.link); + output->base.current_mode = &output->mode; + + wl_list_init(&output->shm.buffers); + wl_list_init(&output->shm.free_buffers); + + weston_output_init(&output->base, &c->base, x, y, width, height, + transform, scale); + + if (c->use_pixman) { + if (wayland_output_init_pixman_renderer(output) < 0) + goto err_output; + output->base.repaint = wayland_output_repaint_pixman; + } else { + if (wayland_output_init_gl_renderer(output) < 0) + goto err_output; + output->base.repaint = wayland_output_repaint_gl; + } output->base.start_repaint_loop = wayland_output_start_repaint_loop; - output->base.repaint = wayland_output_repaint; output->base.destroy = wayland_output_destroy; output->base.assign_planes = NULL; output->base.set_backlight = NULL; @@ -305,15 +829,96 @@ wayland_compositor_create_output(struct wayland_compositor *c, wl_list_insert(c->base.output_list.prev, &output->base.link); - return 0; + return output; + +err_output: + weston_output_destroy(&output->base); + wl_shell_surface_destroy(output->parent.shell_surface); +err_surface: + wl_surface_destroy(output->parent.surface); +err_name: + free(output->name); -cleanup_window: - wl_egl_window_destroy(output->parent.egl_window); -cleanup_output: /* FIXME: cleanup weston_output */ free(output); - return -1; + return NULL; +} + +static struct wayland_output * +wayland_output_create_for_config(struct wayland_compositor *c, + struct weston_config_section *config_section, + int option_width, int option_height, + int option_scale, int32_t x, int32_t y) +{ + struct wayland_output *output; + char *mode, *t, *name, *str; + int width, height, scale; + uint32_t transform; + unsigned int i, slen; + + static const struct { const char *name; uint32_t token; } transform_names[] = { + { "normal", WL_OUTPUT_TRANSFORM_NORMAL }, + { "90", WL_OUTPUT_TRANSFORM_90 }, + { "180", WL_OUTPUT_TRANSFORM_180 }, + { "270", WL_OUTPUT_TRANSFORM_270 }, + { "flipped", WL_OUTPUT_TRANSFORM_FLIPPED }, + { "flipped-90", WL_OUTPUT_TRANSFORM_FLIPPED_90 }, + { "flipped-180", WL_OUTPUT_TRANSFORM_FLIPPED_180 }, + { "flipped-270", WL_OUTPUT_TRANSFORM_FLIPPED_270 }, + }; + + weston_config_section_get_string(config_section, "name", &name, NULL); + if (name) { + slen = strlen(name); + slen += strlen(WINDOW_TITLE " - "); + str = malloc(slen + 1); + if (str) + snprintf(str, slen + 1, WINDOW_TITLE " - %s", name); + free(name); + name = str; + } + if (!name) + name = WINDOW_TITLE; + + weston_config_section_get_string(config_section, + "mode", &mode, "1024x600"); + if (sscanf(mode, "%dx%d", &width, &height) != 2) { + weston_log("Invalid mode \"%s\" for output %s\n", + mode, name); + width = 1024; + height = 640; + } + free(mode); + + if (option_width) + width = option_width; + if (option_height) + height = option_height; + + weston_config_section_get_int(config_section, "scale", &scale, 1); + + if (option_scale) + scale = option_scale; + + weston_config_section_get_string(config_section, + "transform", &t, "normal"); + transform = WL_OUTPUT_TRANSFORM_NORMAL; + for (i = 0; i < ARRAY_LENGTH(transform_names); i++) { + if (strcmp(transform_names[i].name, t) == 0) { + transform = transform_names[i].token; + break; + } + } + if (i >= ARRAY_LENGTH(transform_names)) + weston_log("Invalid transform \"%s\" for output %s\n", t, name); + free(t); + + output = wayland_output_create(c, x, y, width, height, name, 0, + transform, scale); + free(name); + + return output; } static void @@ -327,6 +932,11 @@ static void shell_surface_configure(void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height) { + struct wayland_output *output = data; + + output->parent.configure_width = width; + output->parent.configure_height = height; + /* FIXME: implement resizing */ } @@ -343,85 +953,71 @@ static const struct wl_shell_surface_listener shell_surface_listener = { /* Events received from the wayland-server this compositor is client of: */ -/* parent output interface */ -static void -display_handle_geometry(void *data, - struct wl_output *wl_output, - int x, - int y, - int physical_width, - int physical_height, - int subpixel, - const char *make, - const char *model, - int transform) -{ - struct wayland_compositor *c = data; - - c->parent.screen_allocation.x = x; - c->parent.screen_allocation.y = y; -} - +/* parent input interface */ static void -display_handle_mode(void *data, - struct wl_output *wl_output, - uint32_t flags, - int width, - int height, - int refresh) +input_set_cursor(struct wayland_input *input) { - struct wayland_compositor *c = data; - c->parent.screen_allocation.width = width; - c->parent.screen_allocation.height = height; -} + struct wl_buffer *buffer; + struct wl_cursor_image *image; -static const struct wl_output_listener output_listener = { - display_handle_geometry, - display_handle_mode -}; + if (!input->compositor->cursor) + return; /* Couldn't load the cursor. Can't set it */ -static void -check_focus(struct wayland_input *input, wl_fixed_t x, wl_fixed_t y) -{ - struct wayland_compositor *c = input->compositor; - int width, height, inside; + image = input->compositor->cursor->images[0]; + buffer = wl_cursor_image_get_buffer(image); - width = input->output->mode.width; - height = input->output->mode.height; - inside = c->border.left <= wl_fixed_to_int(x) && - wl_fixed_to_int(x) < width + c->border.left && - c->border.top <= wl_fixed_to_int(y) && - wl_fixed_to_int(y) < height + c->border.top; + wl_pointer_set_cursor(input->parent.pointer, input->enter_serial, + input->parent.cursor.surface, + image->hotspot_x, image->hotspot_y); - if (!input->focus && inside) { - notify_pointer_focus(&input->base, &input->output->base, - x - wl_fixed_from_int(c->border.left), - y = wl_fixed_from_int(c->border.top)); - wl_pointer_set_cursor(input->pointer, - input->enter_serial, NULL, 0, 0); - } else if (input->focus && !inside) { - notify_pointer_focus(&input->base, NULL, 0, 0); - /* FIXME: Should set default cursor here. */ - } - - input->focus = inside; + wl_surface_attach(input->parent.cursor.surface, buffer, 0, 0); + wl_surface_damage(input->parent.cursor.surface, 0, 0, + image->width, image->height); + wl_surface_commit(input->parent.cursor.surface); } -/* parent input interface */ static void input_handle_pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y) { struct wayland_input *input = data; + int32_t fx, fy; + enum theme_location location; /* XXX: If we get a modifier event immediately before the focus, * we should try to keep the same serial. */ input->enter_serial = serial; input->output = wl_surface_get_user_data(surface); - check_focus(input, x, y); + + if (input->output->frame) { + location = frame_pointer_enter(input->output->frame, input, + wl_fixed_to_int(x), + wl_fixed_to_int(y)); + frame_interior(input->output->frame, &fx, &fy, NULL, NULL); + x -= wl_fixed_from_int(fx); + y -= wl_fixed_from_int(fy); + + if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT) + weston_output_schedule_repaint(&input->output->base); + } else { + location = THEME_LOCATION_CLIENT_AREA; + } + + weston_output_transform_coordinate(&input->output->base, x, y, &x, &y); + + if (location == THEME_LOCATION_CLIENT_AREA) { + input->focus = 1; + notify_pointer_focus(&input->base, &input->output->base, x, y); + wl_pointer_set_cursor(input->parent.pointer, + input->enter_serial, NULL, 0, 0); + } else { + input->focus = 0; + notify_pointer_focus(&input->base, NULL, 0, 0); + input_set_cursor(input); + } } static void @@ -430,6 +1026,13 @@ input_handle_pointer_leave(void *data, struct wl_pointer *pointer, { struct wayland_input *input = data; + if (input->output->frame) { + frame_pointer_leave(input->output->frame, input); + + if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT) + weston_output_schedule_repaint(&input->output->base); + } + notify_pointer_focus(&input->base, NULL, 0, 0); input->output = NULL; input->focus = 0; @@ -440,15 +1043,38 @@ input_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { struct wayland_input *input = data; - struct wayland_compositor *c = input->compositor; + int32_t fx, fy; + enum theme_location location; + + if (input->output->frame) { + location = frame_pointer_motion(input->output->frame, input, + wl_fixed_to_int(x), + wl_fixed_to_int(y)); + frame_interior(input->output->frame, &fx, &fy, NULL, NULL); + x -= wl_fixed_from_int(fx); + y -= wl_fixed_from_int(fy); + + if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT) + weston_output_schedule_repaint(&input->output->base); + } else { + location = THEME_LOCATION_CLIENT_AREA; + } + + weston_output_transform_coordinate(&input->output->base, x, y, &x, &y); + + if (input->focus && location != THEME_LOCATION_CLIENT_AREA) { + input_set_cursor(input); + notify_pointer_focus(&input->base, NULL, 0, 0); + input->focus = 0; + } else if (!input->focus && location == THEME_LOCATION_CLIENT_AREA) { + wl_pointer_set_cursor(input->parent.pointer, + input->enter_serial, NULL, 0, 0); + notify_pointer_focus(&input->base, &input->output->base, x, y); + input->focus = 1; + } - check_focus(input, x, y); - if (input->focus) - notify_motion(&input->base, time, - x - wl_fixed_from_int(c->border.left) - - input->base.pointer->x, - y - wl_fixed_from_int(c->border.top) - - input->base.pointer->y); + if (location == THEME_LOCATION_CLIENT_AREA) + notify_motion_absolute(&input->base, time, x, y); } static void @@ -458,8 +1084,36 @@ input_handle_button(void *data, struct wl_pointer *pointer, { struct wayland_input *input = data; enum wl_pointer_button_state state = state_w; + enum frame_button_state fstate; + enum theme_location location; - notify_button(&input->base, time, button, state); + if (input->output->frame) { + fstate = state == WL_POINTER_BUTTON_STATE_PRESSED ? + FRAME_BUTTON_PRESSED : FRAME_BUTTON_RELEASED; + + location = frame_pointer_button(input->output->frame, input, + button, fstate); + + if (frame_status(input->output->frame) & FRAME_STATUS_MOVE) { + + wl_shell_surface_move(input->output->parent.shell_surface, + input->parent.seat, serial); + frame_status_clear(input->output->frame, + FRAME_STATUS_MOVE); + return; + } + + if (frame_status(input->output->frame) & FRAME_STATUS_CLOSE) + wl_display_terminate(input->compositor->base.wl_display); + + if (frame_status(input->output->frame) & FRAME_STATUS_REPAINT) + weston_output_schedule_repaint(&input->output->base); + } else { + location = THEME_LOCATION_CLIENT_AREA; + } + + if (location == THEME_LOCATION_CLIENT_AREA) + notify_button(&input->base, time, button, state); } static void @@ -515,7 +1169,11 @@ input_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, return; } - weston_seat_init_keyboard(&input->base, keymap); + if (input->base.keyboard) + weston_seat_update_keymap(&input->base, keymap); + else + weston_seat_init_keyboard(&input->base, keymap); + xkb_map_unref(keymap); } @@ -527,6 +1185,28 @@ input_handle_keyboard_enter(void *data, struct wl_array *keys) { struct wayland_input *input = data; + struct wayland_output *focus; + + focus = input->keyboard_focus; + if (focus) { + /* This shouldn't happen */ + focus->keyboard_count--; + if (!focus->keyboard_count && focus->frame) + frame_unset_flag(focus->frame, FRAME_FLAG_ACTIVE); + if (frame_status(focus->frame) & FRAME_STATUS_REPAINT) + weston_output_schedule_repaint(&focus->base); + } + + input->keyboard_focus = wl_surface_get_user_data(surface); + input->keyboard_focus->keyboard_count++; + + focus = input->keyboard_focus; + if (focus->frame) { + frame_set_flag(focus->frame, FRAME_FLAG_ACTIVE); + if (frame_status(focus->frame) & FRAME_STATUS_REPAINT) + weston_output_schedule_repaint(&focus->base); + } + /* XXX: If we get a modifier event immediately before the focus, * we should try to keep the same serial. */ @@ -541,8 +1221,22 @@ input_handle_keyboard_leave(void *data, struct wl_surface *surface) { struct wayland_input *input = data; + struct wayland_output *focus; notify_keyboard_focus_out(&input->base); + + focus = input->keyboard_focus; + if (!focus) + return; /* This shouldn't happen */ + + focus->keyboard_count--; + if (!focus->keyboard_count && focus->frame) { + frame_unset_flag(focus->frame, FRAME_FLAG_ACTIVE); + if (frame_status(focus->frame) & FRAME_STATUS_REPAINT) + weston_output_schedule_repaint(&focus->base); + } + + input->keyboard_focus = NULL; } static void @@ -576,7 +1270,7 @@ input_handle_modifiers(void *data, struct wl_keyboard *keyboard, else serial_out = wl_display_next_serial(c->base.wl_display); - xkb_state_update_mask(input->base.xkb_state.state, + xkb_state_update_mask(input->base.keyboard->xkb_state.state, mods_depressed, mods_latched, mods_locked, 0, 0, group); notify_modifiers(&input->base, serial_out); @@ -596,25 +1290,25 @@ input_handle_capabilities(void *data, struct wl_seat *seat, { struct wayland_input *input = data; - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) { - input->pointer = wl_seat_get_pointer(seat); - wl_pointer_set_user_data(input->pointer, input); - wl_pointer_add_listener(input->pointer, &pointer_listener, - input); + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->parent.pointer) { + input->parent.pointer = wl_seat_get_pointer(seat); + wl_pointer_set_user_data(input->parent.pointer, input); + wl_pointer_add_listener(input->parent.pointer, + &pointer_listener, input); weston_seat_init_pointer(&input->base); - } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) { - wl_pointer_destroy(input->pointer); - input->pointer = NULL; + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->parent.pointer) { + wl_pointer_destroy(input->parent.pointer); + input->parent.pointer = NULL; } - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) { - input->keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_set_user_data(input->keyboard, input); - wl_keyboard_add_listener(input->keyboard, &keyboard_listener, - input); - } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) { - wl_keyboard_destroy(input->keyboard); - input->keyboard = NULL; + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->parent.keyboard) { + input->parent.keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_set_user_data(input->parent.keyboard, input); + wl_keyboard_add_listener(input->parent.keyboard, + &keyboard_listener, input); + } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->parent.keyboard) { + wl_keyboard_destroy(input->parent.keyboard); + input->parent.keyboard = NULL; } } @@ -633,12 +1327,15 @@ display_add_seat(struct wayland_compositor *c, uint32_t id) weston_seat_init(&input->base, &c->base, "default"); input->compositor = c; - input->seat = wl_registry_bind(c->parent.registry, id, - &wl_seat_interface, 1); + input->parent.seat = wl_registry_bind(c->parent.registry, id, + &wl_seat_interface, 1); wl_list_insert(c->input_list.prev, &input->link); - wl_seat_add_listener(input->seat, &seat_listener, input); - wl_seat_set_user_data(input->seat, input); + wl_seat_add_listener(input->parent.seat, &seat_listener, input); + wl_seat_set_user_data(input->parent.seat, input); + + input->parent.cursor.surface = + wl_compositor_create_surface(c->parent.compositor); } static void @@ -651,11 +1348,6 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, c->parent.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); - } else if (strcmp(interface, "wl_output") == 0) { - c->parent.output = - wl_registry_bind(registry, name, - &wl_output_interface, 1); - wl_output_add_listener(c->parent.output, &output_listener, c); } else if (strcmp(interface, "wl_shell") == 0) { c->parent.shell = wl_registry_bind(registry, name, @@ -678,6 +1370,11 @@ wayland_compositor_handle_event(int fd, uint32_t mask, void *data) struct wayland_compositor *c = data; int count = 0; + if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { + wl_display_terminate(c->base.wl_display); + return 0; + } + if (mask & WL_EVENT_READABLE) count = wl_display_dispatch(c->parent.wl_display); if (mask & WL_EVENT_WRITABLE) @@ -701,8 +1398,6 @@ wayland_destroy(struct weston_compositor *ec) { struct wayland_compositor *c = (struct wayland_compositor *) ec; - ec->renderer->destroy(ec); - weston_compositor_shutdown(ec); if (c->parent.shm) @@ -711,10 +1406,62 @@ wayland_destroy(struct weston_compositor *ec) free(ec); } -static struct weston_compositor * -wayland_compositor_create(struct wl_display *display, - int width, int height, const char *display_name, - int *argc, char *argv[], +static const char *left_ptrs[] = { + "left_ptr", + "default", + "top_left_arrow", + "left-arrow" +}; + +static void +create_cursor(struct wayland_compositor *c, struct weston_config *config) +{ + struct weston_config_section *s; + int size; + char *theme = NULL; + unsigned int i; + + s = weston_config_get_section(config, "shell", NULL, NULL); + weston_config_section_get_string(s, "cursor-theme", &theme, NULL); + weston_config_section_get_int(s, "cursor-size", &size, 32); + + c->cursor_theme = wl_cursor_theme_load(theme, size, c->parent.shm); + + c->cursor = NULL; + for (i = 0; !c->cursor && i < ARRAY_LENGTH(left_ptrs); ++i) + c->cursor = wl_cursor_theme_get_cursor(c->cursor_theme, + left_ptrs[i]); + if (!c->cursor) { + fprintf(stderr, "could not load left cursor\n"); + return; + } +} + +static void +fullscreen_binding(struct weston_seat *seat_base, uint32_t time, uint32_t key, + void *data) +{ + struct wayland_compositor *c = data; + struct wayland_input *input = NULL; + + wl_list_for_each(input, &c->input_list, link) + if (&input->base == seat_base) + break; + + if (!input || !input->output) + return; + + if (input->output->frame) + wayland_output_set_fullscreen(input->output, 0, 0, NULL); + else + wayland_output_set_windowed(input->output); + + weston_output_schedule_repaint(&input->output->base); +} + +static struct wayland_compositor * +wayland_compositor_create(struct wl_display *display, int use_pixman, + const char *display_name, int *argc, char *argv[], struct weston_config *config) { struct wayland_compositor *c; @@ -739,29 +1486,40 @@ wayland_compositor_create(struct wl_display *display, wl_list_init(&c->input_list); c->parent.registry = wl_display_get_registry(c->parent.wl_display); wl_registry_add_listener(c->parent.registry, ®istry_listener, c); - wl_display_dispatch(c->parent.wl_display); + wl_display_roundtrip(c->parent.wl_display); + + create_cursor(c, config); c->base.wl_display = display; - if (gl_renderer_create(&c->base, c->parent.wl_display, - gl_renderer_alpha_attribs, - NULL) < 0) - goto err_display; - c->base.destroy = wayland_destroy; - c->base.restore = wayland_restore; + c->use_pixman = use_pixman; - c->border.top = 30; - c->border.bottom = 24; - c->border.left = 25; - c->border.right = 26; + if (!c->use_pixman) { + gl_renderer = weston_load_module("gl-renderer.so", + "gl_renderer_interface"); + if (!gl_renderer) + c->use_pixman = 1; + } + + if (!c->use_pixman) { + if (gl_renderer->create(&c->base, c->parent.wl_display, + gl_renderer->alpha_attribs, + NULL) < 0) { + weston_log("Failed to initialize the GL renderer; " + "falling back to pixman.\n"); + c->use_pixman = 1; + } + } - /* requires border fields */ - if (wayland_compositor_create_output(c, width, height) < 0) - goto err_gl; + if (c->use_pixman) { + if (pixman_renderer_init(&c->base) < 0) { + weston_log("Failed to initialize pixman renderer\n"); + goto err_display; + } + } - /* requires gl_renderer_output_state_create called - * by wayland_compositor_create_output */ - create_border(c); + c->base.destroy = wayland_destroy; + c->base.restore = wayland_restore; loop = wl_display_get_event_loop(c->base.wl_display); @@ -770,13 +1528,16 @@ wayland_compositor_create(struct wl_display *display, wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, wayland_compositor_handle_event, c); if (c->parent.wl_source == NULL) - goto err_gl; + goto err_renderer; wl_event_source_check(c->parent.wl_source); - return &c->base; + weston_compositor_add_key_binding(&c->base, KEY_F, + MODIFIER_CTRL | MODIFIER_ALT, + fullscreen_binding, c); -err_gl: + return c; +err_renderer: c->base.renderer->destroy(&c->base); err_display: wl_display_disconnect(c->parent.wl_display); @@ -787,22 +1548,120 @@ err_free: return NULL; } +static void +wayland_compositor_destroy(struct wayland_compositor *c) +{ + struct weston_output *output; + + wl_list_for_each(output, &c->base.output_list, link) + wayland_output_destroy(output); + + c->base.renderer->destroy(&c->base); + wl_display_disconnect(c->parent.wl_display); + + if (c->theme) + theme_destroy(c->theme); + if (c->frame_device) + cairo_device_destroy(c->frame_device); + wl_cursor_theme_destroy(c->cursor_theme); + + weston_compositor_shutdown(&c->base); + free(c); +} + WL_EXPORT struct weston_compositor * backend_init(struct wl_display *display, int *argc, char *argv[], struct weston_config *config) { - int width = 1024, height = 640; - char *display_name = NULL; + struct wayland_compositor *c; + struct wayland_output *output; + struct weston_config_section *section; + int x, count, width, height, scale, use_pixman, fullscreen; + const char *section_name, *display_name; + char *name; const struct weston_option wayland_options[] = { { WESTON_OPTION_INTEGER, "width", 0, &width }, { WESTON_OPTION_INTEGER, "height", 0, &height }, + { WESTON_OPTION_INTEGER, "scale", 0, &scale }, { WESTON_OPTION_STRING, "display", 0, &display_name }, + { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &use_pixman }, + { WESTON_OPTION_INTEGER, "output-count", 0, &count }, + { WESTON_OPTION_BOOLEAN, "fullscreen", 0, &fullscreen }, }; + width = 0; + height = 0; + scale = 0; + display_name = NULL; + use_pixman = 0; + count = 1; + fullscreen = 0; parse_options(wayland_options, ARRAY_LENGTH(wayland_options), argc, argv); - return wayland_compositor_create(display, width, height, display_name, - argc, argv, config); + c = wayland_compositor_create(display, use_pixman, display_name, + argc, argv, config); + if (!c) + return NULL; + + if (fullscreen) { + output = wayland_output_create(c, 0, 0, width, height, + NULL, 1, 0, 1); + if (!output) + goto err_outputs; + + wayland_output_set_fullscreen(output, 0, 0, NULL); + return &c->base; + } + + section = NULL; + x = 0; + while (weston_config_next_section(config, §ion, §ion_name)) { + if (!section_name || strcmp(section_name, "output") != 0) + continue; + weston_config_section_get_string(section, "name", &name, NULL); + if (name == NULL) + continue; + + if (name[0] != 'W' || name[1] != 'L') { + free(name); + continue; + } + free(name); + + output = wayland_output_create_for_config(c, section, width, + height, scale, x, 0); + if (!output) + goto err_outputs; + if (wayland_output_set_windowed(output)) + goto err_outputs; + + x += output->base.width; + --count; + } + + if (!width) + width = 1024; + if (!height) + height = 640; + if (!scale) + scale = 1; + while (count > 0) { + output = wayland_output_create(c, x, 0, width, height, + NULL, 0, 0, scale); + if (!output) + goto err_outputs; + if (wayland_output_set_windowed(output)) + goto err_outputs; + + x += width; + --count; + } + + return &c->base; + +err_outputs: + wayland_compositor_destroy(c); + return NULL; } diff --git a/src/compositor-x11.c b/src/compositor-x11.c index e6022f77..6b5eb648 100644 --- a/src/compositor-x11.c +++ b/src/compositor-x11.c @@ -115,6 +115,8 @@ struct x11_output { int32_t scale; }; +struct gl_renderer_interface *gl_renderer; + static struct xkb_keymap * x11_compositor_get_keymap(struct x11_compositor *c) { @@ -160,7 +162,7 @@ x11_compositor_get_keymap(struct x11_compositor *c) static uint32_t get_xkb_mod_mask(struct x11_compositor *c, uint32_t in) { - struct weston_xkb_info *info = c->core_seat.xkb_info; + struct weston_xkb_info *info = c->core_seat.keyboard->xkb_info; uint32_t ret = 0; if ((in & ShiftMask) && info->shift_mod != XKB_MOD_INVALID) @@ -201,6 +203,7 @@ x11_compositor_setup_xkb(struct x11_compositor *c) xcb_xkb_per_client_flags_reply_t *pcf_reply; xcb_xkb_get_state_cookie_t state; xcb_xkb_get_state_reply_t *state_reply; + uint32_t values[1] = { XCB_EVENT_MASK_PROPERTY_CHANGE }; c->has_xkb = 0; c->xkb_event_base = 0; @@ -270,7 +273,7 @@ x11_compositor_setup_xkb(struct x11_compositor *c) return; } - xkb_state_update_mask(c->core_seat.xkb_state.state, + xkb_state_update_mask(c->core_seat.keyboard->xkb_state.state, get_xkb_mod_mask(c, state_reply->baseMods), get_xkb_mod_mask(c, state_reply->latchedMods), get_xkb_mod_mask(c, state_reply->lockedMods), @@ -280,10 +283,29 @@ x11_compositor_setup_xkb(struct x11_compositor *c) free(state_reply); + xcb_change_window_attributes(c->conn, c->screen->root, + XCB_CW_EVENT_MASK, values); + c->has_xkb = 1; #endif } +#ifdef HAVE_XCB_XKB +static void +update_xkb_keymap(struct x11_compositor *c) +{ + struct xkb_keymap *keymap; + + keymap = x11_compositor_get_keymap(c); + if (!keymap) { + weston_log("failed to get XKB keymap\n"); + return; + } + weston_seat_update_keymap(&c->core_seat, keymap); + xkb_keymap_unref(keymap); +} +#endif + static int x11_input_create(struct x11_compositor *c, int no_input) { @@ -346,92 +368,39 @@ set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region struct x11_output *output = (struct x11_output *)output_base; struct weston_compositor *ec = output->base.compositor; struct x11_compositor *c = (struct x11_compositor *)ec; + pixman_region32_t transformed_region; pixman_box32_t *rects; xcb_rectangle_t *output_rects; - pixman_box32_t rect, transformed_rect; xcb_void_cookie_t cookie; - int width, height, nrects, i; + int nrects, i; xcb_generic_error_t *err; - rects = pixman_region32_rectangles(region, &nrects); + pixman_region32_init(&transformed_region); + pixman_region32_copy(&transformed_region, region); + pixman_region32_translate(&transformed_region, + -output_base->x, -output_base->y); + weston_transformed_region(output_base->width, output_base->height, + output_base->transform, + output_base->current_scale, + &transformed_region, &transformed_region); + + rects = pixman_region32_rectangles(&transformed_region, &nrects); output_rects = calloc(nrects, sizeof(xcb_rectangle_t)); - if (output_rects == NULL) + if (output_rects == NULL) { + pixman_region32_fini(&transformed_region); return; - - width = output_base->width; - height = output_base->height; + } for (i = 0; i < nrects; i++) { - rect = rects[i]; - rect.x1 -= output_base->x; - rect.y1 -= output_base->y; - rect.x2 -= output_base->x; - rect.y2 -= output_base->y; - - switch (output_base->transform) { - default: - case WL_OUTPUT_TRANSFORM_NORMAL: - transformed_rect.x1 = rect.x1; - transformed_rect.y1 = rect.y1; - transformed_rect.x2 = rect.x2; - transformed_rect.y2 = rect.y2; - break; - case WL_OUTPUT_TRANSFORM_90: - transformed_rect.x1 = height - rect.y2; - transformed_rect.y1 = rect.x1; - transformed_rect.x2 = height - rect.y1; - transformed_rect.y2 = rect.x2; - break; - case WL_OUTPUT_TRANSFORM_180: - transformed_rect.x1 = width - rect.x2; - transformed_rect.y1 = height - rect.y2; - transformed_rect.x2 = width - rect.x1; - transformed_rect.y2 = height - rect.y1; - break; - case WL_OUTPUT_TRANSFORM_270: - transformed_rect.x1 = rect.y1; - transformed_rect.y1 = width - rect.x2; - transformed_rect.x2 = rect.y2; - transformed_rect.y2 = width - rect.x1; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED: - transformed_rect.x1 = width - rect.x2; - transformed_rect.y1 = rect.y1; - transformed_rect.x2 = width - rect.x1; - transformed_rect.y2 = rect.y2; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - transformed_rect.x1 = height - rect.y2; - transformed_rect.y1 = width - rect.x2; - transformed_rect.x2 = height - rect.y1; - transformed_rect.y2 = width - rect.x1; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - transformed_rect.x1 = rect.x1; - transformed_rect.y1 = height - rect.y2; - transformed_rect.x2 = rect.x2; - transformed_rect.y2 = height - rect.y1; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - transformed_rect.x1 = rect.y1; - transformed_rect.y1 = rect.x1; - transformed_rect.x2 = rect.y2; - transformed_rect.y2 = rect.x2; - break; - } - - transformed_rect.x1 *= output_base->current_scale; - transformed_rect.y1 *= output_base->current_scale; - transformed_rect.x2 *= output_base->current_scale; - transformed_rect.y2 *= output_base->current_scale; - - output_rects[i].x = transformed_rect.x1; - output_rects[i].y = transformed_rect.y1; - output_rects[i].width = transformed_rect.x2 - transformed_rect.x1; - output_rects[i].height = transformed_rect.y2 - transformed_rect.y1; + output_rects[i].x = rects[i].x1; + output_rects[i].y = rects[i].y1; + output_rects[i].width = rects[i].x2 - rects[i].x1; + output_rects[i].height = rects[i].y2 - rects[i].y1; } + pixman_region32_fini(&transformed_region); + cookie = xcb_set_clip_rectangles_checked(c->conn, XCB_CLIP_ORDERING_UNSORTED, output->gc, 0, 0, nrects, @@ -514,14 +483,13 @@ x11_output_destroy(struct weston_output *output_base) struct x11_compositor *compositor = (struct x11_compositor *)output->base.compositor; - wl_list_remove(&output->base.link); wl_event_source_remove(output->finish_frame_timer); if (compositor->use_pixman) { pixman_renderer_output_destroy(output_base); x11_output_deinit_shm(compositor, output); } else - gl_renderer_output_destroy(output_base); + gl_renderer->output_destroy(output_base); xcb_destroy_window(compositor->conn, output->window); @@ -734,6 +702,12 @@ x11_output_init_shm(struct x11_compositor *c, struct x11_output *output, visual_type->blue_mask == 0x0000ff) { weston_log("Will use x8r8g8b8 format for SHM surfaces\n"); pixman_format = PIXMAN_x8r8g8b8; + } else if (bitsperpixel == 16 && + visual_type->red_mask == 0x00f800 && + visual_type->green_mask == 0x0007e0 && + visual_type->blue_mask == 0x00001f) { + weston_log("Will use r5g6b5 format for SHM surfaces\n"); + pixman_format = PIXMAN_r5g6b5; } else { weston_log("Can't find appropriate format for SHM pixmap\n"); errno = ENOTSUP; @@ -787,6 +761,7 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y, struct wm_normal_hints normal_hints; struct wl_event_loop *loop; int output_width, output_height; + int ret; uint32_t mask = XCB_CW_EVENT_MASK | XCB_CW_CURSOR; xcb_atom_t atom_list[1]; uint32_t values[2] = { @@ -896,6 +871,10 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y, output->base.current_mode = &output->mode; output->base.make = "xwayland"; output->base.model = "none"; + + if (configured_name) + output->base.name = strdup(configured_name); + weston_output_init(&output->base, &c->base, x, y, width, height, transform, scale); @@ -909,7 +888,9 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y, return NULL; } } else { - if (gl_renderer_output_create(&output->base, (EGLNativeWindowType)output->window) < 0) + ret = gl_renderer->output_create(&output->base, + (EGLNativeWindowType) output->window); + if (ret < 0) return NULL; } @@ -938,11 +919,25 @@ x11_compositor_find_output(struct x11_compositor *c, xcb_window_t window) assert(0); } +static void +x11_compositor_delete_window(struct x11_compositor *c, xcb_window_t window) +{ + struct x11_output *output; + + output = x11_compositor_find_output(c, window); + x11_output_destroy(&output->base); + + xcb_flush(c->conn); + + if (wl_list_empty(&c->base.output_list)) + wl_display_terminate(c->base.wl_display); +} + #ifdef HAVE_XCB_XKB static void update_xkb_state(struct x11_compositor *c, xcb_xkb_state_notify_event_t *state) { - xkb_state_update_mask(c->core_seat.xkb_state.state, + xkb_state_update_mask(c->core_seat.keyboard->xkb_state.state, get_xkb_mod_mask(c, state->baseMods), get_xkb_mod_mask(c, state->latchedMods), get_xkb_mod_mask(c, state->lockedMods), @@ -972,7 +967,7 @@ update_xkb_state_from_core(struct x11_compositor *c, uint16_t x11_mask) uint32_t mask = get_xkb_mod_mask(c, x11_mask); struct weston_keyboard *keyboard = c->core_seat.keyboard; - xkb_state_update_mask(c->core_seat.xkb_state.state, + xkb_state_update_mask(c->core_seat.keyboard->xkb_state.state, keyboard->modifiers.mods_depressed & mask, keyboard->modifiers.mods_latched & mask, keyboard->modifiers.mods_locked & mask, @@ -1072,8 +1067,9 @@ x11_compositor_deliver_motion_event(struct x11_compositor *c, update_xkb_state_from_core(c, motion_notify->state); output = x11_compositor_find_output(c, motion_notify->event); weston_output_transform_coordinate(&output->base, - motion_notify->event_x, - motion_notify->event_y, &x, &y); + wl_fixed_from_int(motion_notify->event_x), + wl_fixed_from_int(motion_notify->event_y), + &x, &y); notify_motion(&c->core_seat, weston_compositor_get_time(), x - c->prev_x, y - c->prev_y); @@ -1097,8 +1093,8 @@ x11_compositor_deliver_enter_event(struct x11_compositor *c, update_xkb_state_from_core(c, enter_notify->state); output = x11_compositor_find_output(c, enter_notify->event); weston_output_transform_coordinate(&output->base, - enter_notify->event_x, - enter_notify->event_y, &x, &y); + wl_fixed_from_int(enter_notify->event_x), + wl_fixed_from_int(enter_notify->event_y), &x, &y); notify_pointer_focus(&c->core_seat, &output->base, x, y); @@ -1136,6 +1132,7 @@ x11_compositor_handle_event(int fd, uint32_t mask, void *data) xcb_focus_in_event_t *focus_in; xcb_expose_event_t *expose; xcb_atom_t atom; + xcb_window_t window; uint32_t *k; uint32_t i, set; uint8_t response_type; @@ -1244,6 +1241,7 @@ x11_compositor_handle_event(int fd, uint32_t mask, void *data) case XCB_EXPOSE: expose = (xcb_expose_event_t *) event; output = x11_compositor_find_output(c, expose->window); + weston_output_damage(&output->base); weston_output_schedule_repaint(&output->base); break; @@ -1263,8 +1261,9 @@ x11_compositor_handle_event(int fd, uint32_t mask, void *data) case XCB_CLIENT_MESSAGE: client_message = (xcb_client_message_event_t *) event; atom = client_message->data.data32[0]; + window = client_message->window; if (atom == c->atom.wm_delete_window) - wl_display_terminate(c->base.wl_display); + x11_compositor_delete_window(c, window); break; case XCB_FOCUS_IN: @@ -1288,12 +1287,20 @@ x11_compositor_handle_event(int fd, uint32_t mask, void *data) } #ifdef HAVE_XCB_XKB - if (c->has_xkb && - response_type == c->xkb_event_base) { - xcb_xkb_state_notify_event_t *state = - (xcb_xkb_state_notify_event_t *) event; - if (state->xkbType == XCB_XKB_STATE_NOTIFY) - update_xkb_state(c, state); + if (c->has_xkb) { + if (response_type == c->xkb_event_base) { + xcb_xkb_state_notify_event_t *state = + (xcb_xkb_state_notify_event_t *) event; + if (state->xkbType == XCB_XKB_STATE_NOTIFY) + update_xkb_state(c, state); + } else if (response_type == XCB_PROPERTY_NOTIFY) { + xcb_property_notify_event_t *prop_notify = + (xcb_property_notify_event_t *) event; + if (prop_notify->window == c->screen->root && + prop_notify->atom == c->atom.xkb_names && + prop_notify->state == XCB_PROPERTY_NEW_VALUE) + update_xkb_keymap(c); + } } #endif @@ -1414,8 +1421,6 @@ x11_destroy(struct weston_compositor *ec) weston_compositor_shutdown(ec); /* destroys outputs, too */ - ec->renderer->destroy(ec); - XCloseDisplay(compositor->dpy); free(ec); } @@ -1445,6 +1450,21 @@ parse_transform(const char *transform, const char *output_name) return WL_OUTPUT_TRANSFORM_NORMAL; } +static int +init_gl_renderer(struct x11_compositor *c) +{ + int ret; + + gl_renderer = weston_load_module("gl-renderer.so", + "gl_renderer_interface"); + if (!gl_renderer) + return -1; + + ret = gl_renderer->create(&c->base, (EGLNativeDisplayType) c->dpy, + gl_renderer->opaque_attribs, NULL); + + return ret; +} static struct weston_compositor * x11_compositor_create(struct wl_display *display, int fullscreen, @@ -1501,10 +1521,8 @@ x11_compositor_create(struct wl_display *display, if (pixman_renderer_init(&c->base) < 0) goto err_xdisplay; } - else { - if (gl_renderer_create(&c->base, (EGLNativeDisplayType)c->dpy, gl_renderer_opaque_attribs, - NULL) < 0) - goto err_xdisplay; + else if (init_gl_renderer(c) < 0) { + goto err_xdisplay; } weston_log("Using %s renderer\n", use_pixman ? "pixman" : "gl"); diff --git a/src/compositor.c b/src/compositor.c index 36b54b5b..bb1dfa9c 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -53,7 +53,7 @@ #endif #include "compositor.h" -#include "subsurface-server-protocol.h" +#include "scaler-server-protocol.h" #include "../shared/os-compatibility.h" #include "git-version.h" #include "version.h" @@ -93,7 +93,7 @@ weston_output_transform_scale_init(struct weston_output *output, uint32_t transform, uint32_t scale); static void -weston_compositor_build_surface_list(struct weston_compositor *compositor); +weston_compositor_build_view_list(struct weston_compositor *compositor); WL_EXPORT int weston_output_switch_mode(struct weston_output *output, struct weston_mode *mode, @@ -138,7 +138,6 @@ weston_output_switch_mode(struct weston_output *output, struct weston_mode *mode if (ret < 0) return ret; - output->current_mode = mode; output->current_scale = scale; break; case WESTON_MODE_SWITCH_RESTORE_NATIVE: @@ -347,6 +346,125 @@ region_init_infinite(pixman_region32_t *region) static struct weston_subsurface * weston_surface_to_subsurface(struct weston_surface *surface); +static void +weston_view_output_move_handler(struct wl_listener *listener, + void *data) +{ + struct weston_view *ev; + struct weston_output *output = data; + + ev = container_of(listener, struct weston_view, + output_move_listener); + + /* the child window's view->geometry is a relative coordinate to + * parent view, no need to move child_view. */ + if (ev->geometry.parent) + return; + + weston_view_set_position(ev, + ev->geometry.x + output->move_x, + ev->geometry.y + output->move_y); +} + +static void +weston_view_output_destroy_handler(struct wl_listener *listener, + void *data) +{ + struct weston_view *ev; + struct weston_compositor *ec; + struct weston_output *output, *first_output; + float x, y; + int visible; + + ev = container_of(listener, struct weston_view, + output_destroy_listener); + + ec = ev->surface->compositor; + + /* the child window's view->geometry is a relative coordinate to + * parent view, no need to move child_view. */ + if (ev->geometry.parent) + return; + + x = ev->geometry.x; + y = ev->geometry.y; + + /* At this point the destroyed output is not in the list anymore. + * If the view is still visible somewhere, we leave where it is, + * otherwise, move it to the first output. */ + visible = 0; + wl_list_for_each(output, &ec->output_list, link) { + if (pixman_region32_contains_point(&output->region, + x, y, NULL)) { + visible = 1; + break; + } + } + + if (!visible) { + first_output = container_of(ec->output_list.next, + struct weston_output, link); + + x = first_output->x + first_output->width / 4; + y = first_output->y + first_output->height / 4; + } + + weston_view_set_position(ev, x, y); + + if (ev->surface->output_destroyed) + ev->surface->output_destroyed(ev->surface); + + wl_list_remove(&ev->output_move_listener.link); + wl_list_remove(&ev->output_destroy_listener.link); + + wl_list_init(&ev->output_move_listener.link); + wl_list_init(&ev->output_destroy_listener.link); +} + +WL_EXPORT struct weston_view * +weston_view_create(struct weston_surface *surface) +{ + struct weston_view *view; + + view = calloc(1, sizeof *view); + if (view == NULL) + return NULL; + + view->surface = surface; + + /* Assign to surface */ + wl_list_insert(&surface->views, &view->surface_link); + + wl_signal_init(&view->destroy_signal); + wl_list_init(&view->link); + wl_list_init(&view->layer_link); + + view->plane = NULL; + + pixman_region32_init(&view->clip); + + view->alpha = 1.0; + pixman_region32_init(&view->transform.opaque); + + wl_list_init(&view->geometry.transformation_list); + wl_list_insert(&view->geometry.transformation_list, + &view->transform.position.link); + weston_matrix_init(&view->transform.position.matrix); + wl_list_init(&view->geometry.child_list); + pixman_region32_init(&view->transform.boundingbox); + view->transform.dirty = 1; + + view->output = NULL; + + view->output_move_listener.notify = weston_view_output_move_handler; + wl_list_init(&view->output_move_listener.link); + view->output_destroy_listener.notify = + weston_view_output_destroy_handler; + wl_list_init(&view->output_destroy_listener.link); + + return view; +} + WL_EXPORT struct weston_surface * weston_surface_create(struct weston_compositor *compositor) { @@ -360,40 +478,25 @@ weston_surface_create(struct weston_compositor *compositor) surface->resource = NULL; - wl_list_init(&surface->link); - wl_list_init(&surface->layer_link); - surface->compositor = compositor; - surface->alpha = 1.0; surface->ref_count = 1; - if (compositor->renderer->create_surface(surface) < 0) { - free(surface); - return NULL; - } - - surface->buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL; - surface->buffer_scale = 1; - surface->pending.buffer_transform = surface->buffer_transform; - surface->pending.buffer_scale = surface->buffer_scale; + surface->buffer_viewport.transform = WL_OUTPUT_TRANSFORM_NORMAL; + surface->buffer_viewport.scale = 1; + surface->buffer_viewport.viewport_set = 0; + surface->pending.buffer_viewport = surface->buffer_viewport; surface->output = NULL; - surface->plane = NULL; surface->pending.newly_attached = 0; + surface->viewport_resource = NULL; + pixman_region32_init(&surface->damage); pixman_region32_init(&surface->opaque); - pixman_region32_init(&surface->clip); region_init_infinite(&surface->input); - pixman_region32_init(&surface->transform.opaque); - wl_list_init(&surface->frame_callback_list); - wl_list_init(&surface->geometry.transformation_list); - wl_list_insert(&surface->geometry.transformation_list, - &surface->transform.position.link); - weston_matrix_init(&surface->transform.position.matrix); - wl_list_init(&surface->geometry.child_list); - pixman_region32_init(&surface->transform.boundingbox); - surface->transform.dirty = 1; + wl_list_init(&surface->views); + + wl_list_init(&surface->frame_callback_list); surface->pending.buffer_destroy_listener.notify = surface_handle_pending_buffer_destroy; @@ -416,13 +519,13 @@ weston_surface_set_color(struct weston_surface *surface, } WL_EXPORT void -weston_surface_to_global_float(struct weston_surface *surface, - float sx, float sy, float *x, float *y) +weston_view_to_global_float(struct weston_view *view, + float sx, float sy, float *x, float *y) { - if (surface->transform.enabled) { + if (view->transform.enabled) { struct weston_vector v = { { sx, sy, 0.0f, 1.0f } }; - weston_matrix_transform(&surface->transform.matrix, &v); + weston_matrix_transform(&view->transform.matrix, &v); if (fabsf(v.f[3]) < 1e-6) { weston_log("warning: numerical instability in " @@ -436,8 +539,8 @@ weston_surface_to_global_float(struct weston_surface *surface, *x = v.f[0] / v.f[3]; *y = v.f[1] / v.f[3]; } else { - *x = sx + surface->geometry.x; - *y = sy + surface->geometry.y; + *x = sx + view->geometry.x; + *y = sy + view->geometry.y; } } @@ -521,15 +624,130 @@ weston_transformed_rect(int width, int height, return ret; } +WL_EXPORT void +weston_transformed_region(int width, int height, + enum wl_output_transform transform, + int32_t scale, + pixman_region32_t *src, pixman_region32_t *dest) +{ + pixman_box32_t *src_rects, *dest_rects; + int nrects, i; + + if (transform == WL_OUTPUT_TRANSFORM_NORMAL && scale == 1) { + if (src != dest) + pixman_region32_copy(dest, src); + return; + } + + src_rects = pixman_region32_rectangles(src, &nrects); + dest_rects = malloc(nrects * sizeof(*dest_rects)); + if (!dest_rects) + return; + + if (transform == WL_OUTPUT_TRANSFORM_NORMAL) { + memcpy(dest_rects, src_rects, nrects * sizeof(*dest_rects)); + } else { + for (i = 0; i < nrects; i++) { + switch (transform) { + default: + case WL_OUTPUT_TRANSFORM_NORMAL: + dest_rects[i].x1 = src_rects[i].x1; + dest_rects[i].y1 = src_rects[i].y1; + dest_rects[i].x2 = src_rects[i].x2; + dest_rects[i].y2 = src_rects[i].y2; + break; + case WL_OUTPUT_TRANSFORM_90: + dest_rects[i].x1 = height - src_rects[i].y2; + dest_rects[i].y1 = src_rects[i].x1; + dest_rects[i].x2 = height - src_rects[i].y1; + dest_rects[i].y2 = src_rects[i].x2; + break; + case WL_OUTPUT_TRANSFORM_180: + dest_rects[i].x1 = width - src_rects[i].x2; + dest_rects[i].y1 = height - src_rects[i].y2; + dest_rects[i].x2 = width - src_rects[i].x1; + dest_rects[i].y2 = height - src_rects[i].y1; + break; + case WL_OUTPUT_TRANSFORM_270: + dest_rects[i].x1 = src_rects[i].y1; + dest_rects[i].y1 = width - src_rects[i].x2; + dest_rects[i].x2 = src_rects[i].y2; + dest_rects[i].y2 = width - src_rects[i].x1; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED: + dest_rects[i].x1 = width - src_rects[i].x2; + dest_rects[i].y1 = src_rects[i].y1; + dest_rects[i].x2 = width - src_rects[i].x1; + dest_rects[i].y2 = src_rects[i].y2; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + dest_rects[i].x1 = height - src_rects[i].y2; + dest_rects[i].y1 = width - src_rects[i].x2; + dest_rects[i].x2 = height - src_rects[i].y1; + dest_rects[i].y2 = width - src_rects[i].x1; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + dest_rects[i].x1 = src_rects[i].x1; + dest_rects[i].y1 = height - src_rects[i].y2; + dest_rects[i].x2 = src_rects[i].x2; + dest_rects[i].y2 = height - src_rects[i].y1; + break; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + dest_rects[i].x1 = src_rects[i].y1; + dest_rects[i].y1 = src_rects[i].x1; + dest_rects[i].x2 = src_rects[i].y2; + dest_rects[i].y2 = src_rects[i].x2; + break; + } + } + } + + if (scale != 1) { + for (i = 0; i < nrects; i++) { + dest_rects[i].x1 *= scale; + dest_rects[i].x2 *= scale; + dest_rects[i].y1 *= scale; + dest_rects[i].y2 *= scale; + } + } + + pixman_region32_clear(dest); + pixman_region32_init_rects(dest, dest_rects, nrects); + free(dest_rects); +} + +static void +scaler_surface_to_buffer(struct weston_surface *surface, + float sx, float sy, float *bx, float *by) +{ + if (surface->buffer_viewport.viewport_set) { + double a, b; + + a = sx / surface->buffer_viewport.dst_width; + b = a * wl_fixed_to_double(surface->buffer_viewport.src_width); + *bx = b + wl_fixed_to_double(surface->buffer_viewport.src_x); + + a = sy / surface->buffer_viewport.dst_height; + b = a * wl_fixed_to_double(surface->buffer_viewport.src_height); + *by = b + wl_fixed_to_double(surface->buffer_viewport.src_y); + } else { + *bx = sx; + *by = sy; + } +} + WL_EXPORT void weston_surface_to_buffer_float(struct weston_surface *surface, float sx, float sy, float *bx, float *by) { - weston_transformed_coord(surface->geometry.width, - surface->geometry.height, - surface->buffer_transform, - surface->buffer_scale, - sx, sy, bx, by); + /* first transform coordinates if the scaler is set */ + scaler_surface_to_buffer(surface, sx, sy, bx, by); + + weston_transformed_coord(surface->width, + surface->height, + surface->buffer_viewport.transform, + surface->buffer_viewport.scale, + *bx, *by, bx, by); } WL_EXPORT void @@ -538,11 +756,9 @@ weston_surface_to_buffer(struct weston_surface *surface, { float bxf, byf; - weston_transformed_coord(surface->geometry.width, - surface->geometry.height, - surface->buffer_transform, - surface->buffer_scale, - sx, sy, &bxf, &byf); + weston_surface_to_buffer_float(surface, + sx, sy, &bxf, &byf); + *bx = floorf(bxf); *by = floorf(byf); } @@ -551,37 +767,49 @@ WL_EXPORT pixman_box32_t weston_surface_to_buffer_rect(struct weston_surface *surface, pixman_box32_t rect) { - return weston_transformed_rect(surface->geometry.width, - surface->geometry.height, - surface->buffer_transform, - surface->buffer_scale, + float xf, yf; + + /* first transform box coordinates if the scaler is set */ + scaler_surface_to_buffer(surface, rect.x1, rect.y1, &xf, &yf); + rect.x1 = floorf(xf); + rect.y1 = floorf(yf); + + scaler_surface_to_buffer(surface, rect.x2, rect.y2, &xf, &yf); + rect.x2 = floorf(xf); + rect.y2 = floorf(yf); + + return weston_transformed_rect(surface->width, + surface->height, + surface->buffer_viewport.transform, + surface->buffer_viewport.scale, rect); } WL_EXPORT void -weston_surface_move_to_plane(struct weston_surface *surface, +weston_view_move_to_plane(struct weston_view *view, struct weston_plane *plane) { - if (surface->plane == plane) + if (view->plane == plane) return; - weston_surface_damage_below(surface); - surface->plane = plane; - weston_surface_damage(surface); + weston_view_damage_below(view); + view->plane = plane; + weston_surface_damage(view->surface); } WL_EXPORT void -weston_surface_damage_below(struct weston_surface *surface) +weston_view_damage_below(struct weston_view *view) { pixman_region32_t damage; pixman_region32_init(&damage); - pixman_region32_subtract(&damage, &surface->transform.boundingbox, - &surface->clip); - if (surface->plane) - pixman_region32_union(&surface->plane->damage, - &surface->plane->damage, &damage); + pixman_region32_subtract(&damage, &view->transform.boundingbox, + &view->clip); + if (view->plane) + pixman_region32_union(&view->plane->damage, + &view->plane->damage, &damage); pixman_region32_fini(&damage); + weston_view_schedule_repaint(view); } static void @@ -616,10 +844,47 @@ weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask) } } + static void weston_surface_assign_output(struct weston_surface *es) { - struct weston_compositor *ec = es->compositor; + struct weston_output *new_output; + struct weston_view *view; + pixman_region32_t region; + uint32_t max, area, mask; + pixman_box32_t *e; + + new_output = NULL; + max = 0; + mask = 0; + pixman_region32_init(®ion); + wl_list_for_each(view, &es->views, surface_link) { + if (!view->output) + continue; + + pixman_region32_intersect(®ion, &view->transform.boundingbox, + &view->output->region); + + e = pixman_region32_extents(®ion); + area = (e->x2 - e->x1) * (e->y2 - e->y1); + + mask |= view->output_mask; + + if (area >= max) { + new_output = view->output; + max = area; + } + } + pixman_region32_fini(®ion); + + es->output = new_output; + weston_surface_update_output_mask(es, mask); +} + +static void +weston_view_assign_output(struct weston_view *ev) +{ + struct weston_compositor *ec = ev->surface->compositor; struct weston_output *output, *new_output; pixman_region32_t region; uint32_t max, area, mask; @@ -630,7 +895,7 @@ weston_surface_assign_output(struct weston_surface *es) mask = 0; pixman_region32_init(®ion); wl_list_for_each(output, &ec->output_list, link) { - pixman_region32_intersect(®ion, &es->transform.boundingbox, + pixman_region32_intersect(®ion, &ev->transform.boundingbox, &output->region); e = pixman_region32_extents(®ion); @@ -646,14 +911,31 @@ weston_surface_assign_output(struct weston_surface *es) } pixman_region32_fini(®ion); - es->output = new_output; - weston_surface_update_output_mask(es, mask); + if (ev->output_mask != 0) { + wl_list_remove(&ev->output_move_listener.link); + wl_list_remove(&ev->output_destroy_listener.link); + } + + if (mask != 0) { + wl_signal_add(&new_output->move_signal, + &ev->output_move_listener); + wl_signal_add(&new_output->destroy_signal, + &ev->output_destroy_listener); + } else { + wl_list_init(&ev->output_move_listener.link); + wl_list_init(&ev->output_destroy_listener.link); + } + + ev->output = new_output; + ev->output_mask = mask; + + weston_surface_assign_output(ev->surface); } static void -surface_compute_bbox(struct weston_surface *surface, int32_t sx, int32_t sy, - int32_t width, int32_t height, - pixman_region32_t *bbox) +view_compute_bbox(struct weston_view *view, int32_t sx, int32_t sy, + int32_t width, int32_t height, + pixman_region32_t *bbox) { float min_x = HUGE_VALF, min_y = HUGE_VALF; float max_x = -HUGE_VALF, max_y = -HUGE_VALF; @@ -674,8 +956,7 @@ surface_compute_bbox(struct weston_surface *surface, int32_t sx, int32_t sy, for (i = 0; i < 4; ++i) { float x, y; - weston_surface_to_global_float(surface, - s[i][0], s[i][1], &x, &y); + weston_view_to_global_float(view, s[i][0], s[i][1], &x, &y); if (x < min_x) min_x = x; if (x > max_x) @@ -693,57 +974,57 @@ surface_compute_bbox(struct weston_surface *surface, int32_t sx, int32_t sy, } static void -weston_surface_update_transform_disable(struct weston_surface *surface) +weston_view_update_transform_disable(struct weston_view *view) { - surface->transform.enabled = 0; + view->transform.enabled = 0; /* round off fractions when not transformed */ - surface->geometry.x = roundf(surface->geometry.x); - surface->geometry.y = roundf(surface->geometry.y); + view->geometry.x = roundf(view->geometry.x); + view->geometry.y = roundf(view->geometry.y); /* Otherwise identity matrix, but with x and y translation. */ - surface->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE; - surface->transform.position.matrix.d[12] = surface->geometry.x; - surface->transform.position.matrix.d[13] = surface->geometry.y; - - surface->transform.matrix = surface->transform.position.matrix; - - surface->transform.inverse = surface->transform.position.matrix; - surface->transform.inverse.d[12] = -surface->geometry.x; - surface->transform.inverse.d[13] = -surface->geometry.y; - - pixman_region32_init_rect(&surface->transform.boundingbox, - surface->geometry.x, - surface->geometry.y, - surface->geometry.width, - surface->geometry.height); - - if (surface->alpha == 1.0) { - pixman_region32_copy(&surface->transform.opaque, - &surface->opaque); - pixman_region32_translate(&surface->transform.opaque, - surface->geometry.x, - surface->geometry.y); + view->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE; + view->transform.position.matrix.d[12] = view->geometry.x; + view->transform.position.matrix.d[13] = view->geometry.y; + + view->transform.matrix = view->transform.position.matrix; + + view->transform.inverse = view->transform.position.matrix; + view->transform.inverse.d[12] = -view->geometry.x; + view->transform.inverse.d[13] = -view->geometry.y; + + pixman_region32_init_rect(&view->transform.boundingbox, + view->geometry.x, + view->geometry.y, + view->surface->width, + view->surface->height); + + if (view->alpha == 1.0) { + pixman_region32_copy(&view->transform.opaque, + &view->surface->opaque); + pixman_region32_translate(&view->transform.opaque, + view->geometry.x, + view->geometry.y); } } static int -weston_surface_update_transform_enable(struct weston_surface *surface) +weston_view_update_transform_enable(struct weston_view *view) { - struct weston_surface *parent = surface->geometry.parent; - struct weston_matrix *matrix = &surface->transform.matrix; - struct weston_matrix *inverse = &surface->transform.inverse; + struct weston_view *parent = view->geometry.parent; + struct weston_matrix *matrix = &view->transform.matrix; + struct weston_matrix *inverse = &view->transform.inverse; struct weston_transform *tform; - surface->transform.enabled = 1; + view->transform.enabled = 1; /* Otherwise identity matrix, but with x and y translation. */ - surface->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE; - surface->transform.position.matrix.d[12] = surface->geometry.x; - surface->transform.position.matrix.d[13] = surface->geometry.y; + view->transform.position.matrix.type = WESTON_MATRIX_TRANSFORM_TRANSLATE; + view->transform.position.matrix.d[12] = view->geometry.x; + view->transform.position.matrix.d[13] = view->geometry.y; weston_matrix_init(matrix); - wl_list_for_each(tform, &surface->geometry.transformation_list, link) + wl_list_for_each(tform, &view->geometry.transformation_list, link) weston_matrix_multiply(matrix, &tform->matrix); if (parent) @@ -751,143 +1032,144 @@ weston_surface_update_transform_enable(struct weston_surface *surface) if (weston_matrix_invert(inverse, matrix) < 0) { /* Oops, bad total transformation, not invertible */ - weston_log("error: weston_surface %p" - " transformation not invertible.\n", surface); + weston_log("error: weston_view %p" + " transformation not invertible.\n", view); return -1; } - surface_compute_bbox(surface, 0, 0, surface->geometry.width, - surface->geometry.height, - &surface->transform.boundingbox); + view_compute_bbox(view, 0, 0, + view->surface->width, view->surface->height, + &view->transform.boundingbox); return 0; } WL_EXPORT void -weston_surface_update_transform(struct weston_surface *surface) +weston_view_update_transform(struct weston_view *view) { - struct weston_surface *parent = surface->geometry.parent; + struct weston_view *parent = view->geometry.parent; - if (!surface->transform.dirty) + if (!view->transform.dirty) return; if (parent) - weston_surface_update_transform(parent); + weston_view_update_transform(parent); - surface->transform.dirty = 0; + view->transform.dirty = 0; - weston_surface_damage_below(surface); + weston_view_damage_below(view); - pixman_region32_fini(&surface->transform.boundingbox); - pixman_region32_fini(&surface->transform.opaque); - pixman_region32_init(&surface->transform.opaque); + pixman_region32_fini(&view->transform.boundingbox); + pixman_region32_fini(&view->transform.opaque); + pixman_region32_init(&view->transform.opaque); /* transform.position is always in transformation_list */ - if (surface->geometry.transformation_list.next == - &surface->transform.position.link && - surface->geometry.transformation_list.prev == - &surface->transform.position.link && + if (view->geometry.transformation_list.next == + &view->transform.position.link && + view->geometry.transformation_list.prev == + &view->transform.position.link && !parent) { - weston_surface_update_transform_disable(surface); + weston_view_update_transform_disable(view); } else { - if (weston_surface_update_transform_enable(surface) < 0) - weston_surface_update_transform_disable(surface); + if (weston_view_update_transform_enable(view) < 0) + weston_view_update_transform_disable(view); } - weston_surface_damage_below(surface); + weston_view_damage_below(view); - weston_surface_assign_output(surface); + weston_view_assign_output(view); - wl_signal_emit(&surface->compositor->transform_signal, surface); + wl_signal_emit(&view->surface->compositor->transform_signal, + view->surface); } WL_EXPORT void -weston_surface_geometry_dirty(struct weston_surface *surface) +weston_view_geometry_dirty(struct weston_view *view) { - struct weston_surface *child; + struct weston_view *child; /* - * The invariant: if surface->geometry.dirty, then all surfaces - * in surface->geometry.child_list have geometry.dirty too. + * The invariant: if view->geometry.dirty, then all views + * in view->geometry.child_list have geometry.dirty too. * Corollary: if not parent->geometry.dirty, then all ancestors * are not dirty. */ - if (surface->transform.dirty) + if (view->transform.dirty) return; - surface->transform.dirty = 1; + view->transform.dirty = 1; - wl_list_for_each(child, &surface->geometry.child_list, + wl_list_for_each(child, &view->geometry.child_list, geometry.parent_link) - weston_surface_geometry_dirty(child); + weston_view_geometry_dirty(child); } WL_EXPORT void -weston_surface_to_global_fixed(struct weston_surface *surface, - wl_fixed_t sx, wl_fixed_t sy, - wl_fixed_t *x, wl_fixed_t *y) +weston_view_to_global_fixed(struct weston_view *view, + wl_fixed_t vx, wl_fixed_t vy, + wl_fixed_t *x, wl_fixed_t *y) { float xf, yf; - weston_surface_to_global_float(surface, - wl_fixed_to_double(sx), - wl_fixed_to_double(sy), - &xf, &yf); + weston_view_to_global_float(view, + wl_fixed_to_double(vx), + wl_fixed_to_double(vy), + &xf, &yf); *x = wl_fixed_from_double(xf); *y = wl_fixed_from_double(yf); } WL_EXPORT void -weston_surface_from_global_float(struct weston_surface *surface, - float x, float y, float *sx, float *sy) +weston_view_from_global_float(struct weston_view *view, + float x, float y, float *vx, float *vy) { - if (surface->transform.enabled) { + if (view->transform.enabled) { struct weston_vector v = { { x, y, 0.0f, 1.0f } }; - weston_matrix_transform(&surface->transform.inverse, &v); + weston_matrix_transform(&view->transform.inverse, &v); if (fabsf(v.f[3]) < 1e-6) { weston_log("warning: numerical instability in " - "weston_surface_from_global(), divisor = %g\n", + "weston_view_from_global(), divisor = %g\n", v.f[3]); - *sx = 0; - *sy = 0; + *vx = 0; + *vy = 0; return; } - *sx = v.f[0] / v.f[3]; - *sy = v.f[1] / v.f[3]; + *vx = v.f[0] / v.f[3]; + *vy = v.f[1] / v.f[3]; } else { - *sx = x - surface->geometry.x; - *sy = y - surface->geometry.y; + *vx = x - view->geometry.x; + *vy = y - view->geometry.y; } } WL_EXPORT void -weston_surface_from_global_fixed(struct weston_surface *surface, - wl_fixed_t x, wl_fixed_t y, - wl_fixed_t *sx, wl_fixed_t *sy) +weston_view_from_global_fixed(struct weston_view *view, + wl_fixed_t x, wl_fixed_t y, + wl_fixed_t *vx, wl_fixed_t *vy) { - float sxf, syf; + float vxf, vyf; - weston_surface_from_global_float(surface, - wl_fixed_to_double(x), - wl_fixed_to_double(y), - &sxf, &syf); - *sx = wl_fixed_from_double(sxf); - *sy = wl_fixed_from_double(syf); + weston_view_from_global_float(view, + wl_fixed_to_double(x), + wl_fixed_to_double(y), + &vxf, &vyf); + *vx = wl_fixed_from_double(vxf); + *vy = wl_fixed_from_double(vyf); } WL_EXPORT void -weston_surface_from_global(struct weston_surface *surface, - int32_t x, int32_t y, int32_t *sx, int32_t *sy) +weston_view_from_global(struct weston_view *view, + int32_t x, int32_t y, int32_t *vx, int32_t *vy) { - float sxf, syf; + float vxf, vyf; - weston_surface_from_global_float(surface, x, y, &sxf, &syf); - *sx = floorf(sxf); - *sy = floorf(syf); + weston_view_from_global_float(view, x, y, &vxf, &vyf); + *vx = floorf(vxf); + *vy = floorf(vyf); } WL_EXPORT void @@ -901,67 +1183,77 @@ weston_surface_schedule_repaint(struct weston_surface *surface) } WL_EXPORT void -weston_surface_damage(struct weston_surface *surface) +weston_view_schedule_repaint(struct weston_view *view) { - pixman_region32_union_rect(&surface->damage, &surface->damage, - 0, 0, surface->geometry.width, - surface->geometry.height); + struct weston_output *output; - weston_surface_schedule_repaint(surface); + wl_list_for_each(output, &view->surface->compositor->output_list, link) + if (view->output_mask & (1 << output->id)) + weston_output_schedule_repaint(output); } WL_EXPORT void -weston_surface_configure(struct weston_surface *surface, - float x, float y, int width, int height) +weston_surface_damage(struct weston_surface *surface) { - surface->geometry.x = x; - surface->geometry.y = y; - surface->geometry.width = width; - surface->geometry.height = height; - weston_surface_geometry_dirty(surface); + pixman_region32_union_rect(&surface->damage, &surface->damage, + 0, 0, surface->width, + surface->height); + + weston_surface_schedule_repaint(surface); } WL_EXPORT void -weston_surface_set_position(struct weston_surface *surface, - float x, float y) +weston_view_set_position(struct weston_view *view, float x, float y) { - surface->geometry.x = x; - surface->geometry.y = y; - weston_surface_geometry_dirty(surface); + if (view->geometry.x == x && view->geometry.y == y) + return; + + view->geometry.x = x; + view->geometry.y = y; + weston_view_geometry_dirty(view); } static void transform_parent_handle_parent_destroy(struct wl_listener *listener, void *data) { - struct weston_surface *surface = - container_of(listener, struct weston_surface, + struct weston_view *view = + container_of(listener, struct weston_view, geometry.parent_destroy_listener); - weston_surface_set_transform_parent(surface, NULL); + weston_view_set_transform_parent(view, NULL); } WL_EXPORT void -weston_surface_set_transform_parent(struct weston_surface *surface, - struct weston_surface *parent) +weston_view_set_transform_parent(struct weston_view *view, + struct weston_view *parent) { - if (surface->geometry.parent) { - wl_list_remove(&surface->geometry.parent_destroy_listener.link); - wl_list_remove(&surface->geometry.parent_link); + if (view->geometry.parent) { + wl_list_remove(&view->geometry.parent_destroy_listener.link); + wl_list_remove(&view->geometry.parent_link); } - surface->geometry.parent = parent; + view->geometry.parent = parent; - surface->geometry.parent_destroy_listener.notify = + view->geometry.parent_destroy_listener.notify = transform_parent_handle_parent_destroy; if (parent) { wl_signal_add(&parent->destroy_signal, - &surface->geometry.parent_destroy_listener); + &view->geometry.parent_destroy_listener); wl_list_insert(&parent->geometry.child_list, - &surface->geometry.parent_link); + &view->geometry.parent_link); } - weston_surface_geometry_dirty(surface); + weston_view_geometry_dirty(view); +} + +WL_EXPORT int +weston_view_is_mapped(struct weston_view *view) +{ + if (view->output) + return 1; + else + return 0; } WL_EXPORT int @@ -973,40 +1265,62 @@ weston_surface_is_mapped(struct weston_surface *surface) return 0; } -WL_EXPORT int32_t -weston_surface_buffer_width(struct weston_surface *surface) +static void +surface_set_size(struct weston_surface *surface, int32_t width, int32_t height) { - int32_t width; - switch (surface->buffer_transform) { - case WL_OUTPUT_TRANSFORM_90: - case WL_OUTPUT_TRANSFORM_270: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - width = surface->buffer_ref.buffer->height; - break; - default: - width = surface->buffer_ref.buffer->width; - break; - } - return width / surface->buffer_scale; + struct weston_view *view; + + if (surface->width == width && surface->height == height) + return; + + surface->width = width; + surface->height = height; + + wl_list_for_each(view, &surface->views, surface_link) + weston_view_geometry_dirty(view); +} + +WL_EXPORT void +weston_surface_set_size(struct weston_surface *surface, + int32_t width, int32_t height) +{ + assert(!surface->resource); + surface_set_size(surface, width, height); } -WL_EXPORT int32_t -weston_surface_buffer_height(struct weston_surface *surface) +static void +weston_surface_set_size_from_buffer(struct weston_surface *surface) { - int32_t height; - switch (surface->buffer_transform) { + int32_t width, height; + + if (!surface->buffer_ref.buffer) { + surface_set_size(surface, 0, 0); + return; + } + + if (surface->buffer_viewport.viewport_set) { + surface->width = surface->buffer_viewport.dst_width; + surface->height = surface->buffer_viewport.dst_height; + return; + } + + switch (surface->buffer_viewport.transform) { case WL_OUTPUT_TRANSFORM_90: case WL_OUTPUT_TRANSFORM_270: case WL_OUTPUT_TRANSFORM_FLIPPED_90: case WL_OUTPUT_TRANSFORM_FLIPPED_270: + width = surface->buffer_ref.buffer->height; height = surface->buffer_ref.buffer->width; - break; + break; default: + width = surface->buffer_ref.buffer->width; height = surface->buffer_ref.buffer->height; - break; + break; } - return height / surface->buffer_scale; + + width = width / surface->buffer_viewport.scale; + height = height / surface->buffer_viewport.scale; + surface_set_size(surface, width, height); } WL_EXPORT uint32_t @@ -1019,20 +1333,20 @@ weston_compositor_get_time(void) return tv.tv_sec * 1000 + tv.tv_usec / 1000; } -WL_EXPORT struct weston_surface * -weston_compositor_pick_surface(struct weston_compositor *compositor, - wl_fixed_t x, wl_fixed_t y, - wl_fixed_t *sx, wl_fixed_t *sy) +WL_EXPORT struct weston_view * +weston_compositor_pick_view(struct weston_compositor *compositor, + wl_fixed_t x, wl_fixed_t y, + wl_fixed_t *vx, wl_fixed_t *vy) { - struct weston_surface *surface; + struct weston_view *view; - wl_list_for_each(surface, &compositor->surface_list, link) { - weston_surface_from_global_fixed(surface, x, y, sx, sy); - if (pixman_region32_contains_point(&surface->input, - wl_fixed_to_int(*sx), - wl_fixed_to_int(*sy), + wl_list_for_each(view, &compositor->view_list, link) { + weston_view_from_global_fixed(view, x, y, vx, vy); + if (pixman_region32_contains_point(&view->surface->input, + wl_fixed_to_int(*vx), + wl_fixed_to_int(*vy), NULL)) - return surface; + return view; } return NULL; @@ -1043,7 +1357,7 @@ weston_compositor_repick(struct weston_compositor *compositor) { struct weston_seat *seat; - if (!compositor->focus) + if (!compositor->session_active) return; wl_list_for_each(seat, &compositor->seat_list, link) @@ -1051,30 +1365,51 @@ weston_compositor_repick(struct weston_compositor *compositor) } WL_EXPORT void -weston_surface_unmap(struct weston_surface *surface) +weston_view_unmap(struct weston_view *view) { struct weston_seat *seat; - weston_surface_damage_below(surface); - surface->output = NULL; - surface->plane = NULL; - wl_list_remove(&surface->layer_link); - wl_list_remove(&surface->link); - wl_list_init(&surface->link); + if (!weston_view_is_mapped(view)) + return; + + weston_view_damage_below(view); + view->output = NULL; + view->plane = NULL; + wl_list_remove(&view->layer_link); + wl_list_init(&view->layer_link); + wl_list_remove(&view->link); + wl_list_init(&view->link); + wl_list_remove(&view->output_move_listener.link); + wl_list_init(&view->output_move_listener.link); + wl_list_remove(&view->output_destroy_listener.link); + wl_list_init(&view->output_destroy_listener.link); + view->output_mask = 0; + weston_surface_assign_output(view->surface); + + if (weston_surface_is_mapped(view->surface)) + return; - wl_list_for_each(seat, &surface->compositor->seat_list, link) { - if (seat->keyboard && seat->keyboard->focus == surface) + wl_list_for_each(seat, &view->surface->compositor->seat_list, link) { + if (seat->keyboard && seat->keyboard->focus == view->surface) weston_keyboard_set_focus(seat->keyboard, NULL); - if (seat->pointer && seat->pointer->focus == surface) + if (seat->pointer && seat->pointer->focus == view) weston_pointer_set_focus(seat->pointer, NULL, wl_fixed_from_int(0), wl_fixed_from_int(0)); - if (seat->touch && seat->touch->focus == surface) + if (seat->touch && seat->touch->focus == view) weston_touch_set_focus(seat, NULL); } +} - weston_surface_schedule_repaint(surface); +WL_EXPORT void +weston_surface_unmap(struct weston_surface *surface) +{ + struct weston_view *view; + + wl_list_for_each(view, &surface->views, surface_link) + weston_view_unmap(view); + surface->output = NULL; } struct weston_frame_callback { @@ -1082,24 +1417,47 @@ struct weston_frame_callback { struct wl_list link; }; +WL_EXPORT void +weston_view_destroy(struct weston_view *view) +{ + wl_signal_emit(&view->destroy_signal, view); + + assert(wl_list_empty(&view->geometry.child_list)); + + if (weston_view_is_mapped(view)) { + weston_view_unmap(view); + weston_compositor_build_view_list(view->surface->compositor); + } + + wl_list_remove(&view->link); + wl_list_remove(&view->layer_link); + + pixman_region32_fini(&view->clip); + pixman_region32_fini(&view->transform.boundingbox); + + weston_view_set_transform_parent(view, NULL); + + wl_list_remove(&view->surface_link); + + free(view); +} WL_EXPORT void weston_surface_destroy(struct weston_surface *surface) { - struct weston_compositor *compositor = surface->compositor; struct weston_frame_callback *cb, *next; + struct weston_view *ev, *nv; if (--surface->ref_count > 0) return; wl_signal_emit(&surface->destroy_signal, &surface->resource); - assert(wl_list_empty(&surface->geometry.child_list)); assert(wl_list_empty(&surface->subsurface_list_pending)); assert(wl_list_empty(&surface->subsurface_list)); - if (weston_surface_is_mapped(surface)) - weston_surface_unmap(surface); + wl_list_for_each_safe(ev, nv, &surface->views, surface_link) + weston_view_destroy(ev); wl_list_for_each_safe(cb, next, &surface->pending.frame_callback_list, link) @@ -1114,19 +1472,13 @@ weston_surface_destroy(struct weston_surface *surface) weston_buffer_reference(&surface->buffer_ref, NULL); - compositor->renderer->destroy_surface(surface); - - pixman_region32_fini(&surface->transform.boundingbox); pixman_region32_fini(&surface->damage); pixman_region32_fini(&surface->opaque); - pixman_region32_fini(&surface->clip); pixman_region32_fini(&surface->input); wl_list_for_each_safe(cb, next, &surface->frame_callback_list, link) wl_resource_destroy(cb->resource); - weston_surface_set_transform_parent(surface, NULL); - free(surface); } @@ -1135,6 +1487,10 @@ destroy_surface(struct wl_resource *resource) { struct weston_surface *surface = wl_resource_get_user_data(resource); + /* Set the resource to NULL, since we don't want to leave a + * dangling pointer if the surface was refcounted and survives + * the weston_surface_destroy() call. */ + surface->resource = NULL; weston_surface_destroy(surface); } @@ -1224,15 +1580,6 @@ weston_surface_attach(struct weston_surface *surface, surface->compositor->renderer->attach(surface, buffer); } -WL_EXPORT void -weston_surface_restack(struct weston_surface *surface, struct wl_list *below) -{ - wl_list_remove(&surface->layer_link); - wl_list_insert(below, &surface->layer_link); - weston_surface_damage_below(surface); - weston_surface_damage(surface); -} - WL_EXPORT void weston_compositor_damage_all(struct weston_compositor *compositor) { @@ -1254,43 +1601,53 @@ weston_output_damage(struct weston_output *output) } static void -surface_accumulate_damage(struct weston_surface *surface, - pixman_region32_t *opaque) +surface_flush_damage(struct weston_surface *surface) { if (surface->buffer_ref.buffer && wl_shm_buffer_get(surface->buffer_ref.buffer->resource)) surface->compositor->renderer->flush_damage(surface); - if (surface->transform.enabled) { + empty_region(&surface->damage); +} + +static void +view_accumulate_damage(struct weston_view *view, + pixman_region32_t *opaque) +{ + pixman_region32_t damage; + + pixman_region32_init(&damage); + if (view->transform.enabled) { pixman_box32_t *extents; - extents = pixman_region32_extents(&surface->damage); - surface_compute_bbox(surface, extents->x1, extents->y1, - extents->x2 - extents->x1, - extents->y2 - extents->y1, - &surface->damage); - pixman_region32_translate(&surface->damage, - -surface->plane->x, - -surface->plane->y); + extents = pixman_region32_extents(&view->surface->damage); + view_compute_bbox(view, extents->x1, extents->y1, + extents->x2 - extents->x1, + extents->y2 - extents->y1, + &damage); + pixman_region32_translate(&damage, + -view->plane->x, + -view->plane->y); } else { - pixman_region32_translate(&surface->damage, - surface->geometry.x - surface->plane->x, - surface->geometry.y - surface->plane->y); + pixman_region32_copy(&damage, &view->surface->damage); + pixman_region32_translate(&damage, + view->geometry.x - view->plane->x, + view->geometry.y - view->plane->y); } - pixman_region32_subtract(&surface->damage, &surface->damage, opaque); - pixman_region32_union(&surface->plane->damage, - &surface->plane->damage, &surface->damage); - empty_region(&surface->damage); - pixman_region32_copy(&surface->clip, opaque); - pixman_region32_union(opaque, opaque, &surface->transform.opaque); + pixman_region32_subtract(&damage, &damage, opaque); + pixman_region32_union(&view->plane->damage, + &view->plane->damage, &damage); + pixman_region32_fini(&damage); + pixman_region32_copy(&view->clip, opaque); + pixman_region32_union(opaque, opaque, &view->transform.opaque); } static void compositor_accumulate_damage(struct weston_compositor *ec) { struct weston_plane *plane; - struct weston_surface *es; + struct weston_view *ev; pixman_region32_t opaque, clip; pixman_region32_init(&clip); @@ -1300,11 +1657,11 @@ compositor_accumulate_damage(struct weston_compositor *ec) pixman_region32_init(&opaque); - wl_list_for_each(es, &ec->surface_list, link) { - if (es->plane != plane) + wl_list_for_each(ev, &ec->view_list, link) { + if (ev->plane != plane) continue; - surface_accumulate_damage(es, &opaque); + view_accumulate_damage(ev, &opaque); } pixman_region32_union(&clip, &clip, &opaque); @@ -1313,7 +1670,16 @@ compositor_accumulate_damage(struct weston_compositor *ec) pixman_region32_fini(&clip); - wl_list_for_each(es, &ec->surface_list, link) { + wl_list_for_each(ev, &ec->view_list, link) + ev->surface->touched = 0; + + wl_list_for_each(ev, &ec->view_list, link) { + if (ev->surface->touched) + continue; + ev->surface->touched = 1; + + surface_flush_damage(ev->surface); + /* Both the renderer and the backend have seen the buffer * by now. If renderer needs the buffer, it has its own * reference set. If the backend wants to keep the buffer @@ -1322,77 +1688,161 @@ compositor_accumulate_damage(struct weston_compositor *ec) * reference now, and allow early buffer release. This enables * clients to use single-buffering. */ - if (!es->keep_buffer) - weston_buffer_reference(&es->buffer_ref, NULL); + if (!ev->surface->keep_buffer) + weston_buffer_reference(&ev->surface->buffer_ref, NULL); } } static void -surface_list_add(struct weston_compositor *compositor, - struct weston_surface *surface) +surface_stash_subsurface_views(struct weston_surface *surface) { struct weston_subsurface *sub; - if (wl_list_empty(&surface->subsurface_list)) { - weston_surface_update_transform(surface); - wl_list_insert(compositor->surface_list.prev, &surface->link); - return; + wl_list_for_each(sub, &surface->subsurface_list, parent_link) { + if (sub->surface == surface) + continue; + + wl_list_insert_list(&sub->unused_views, &sub->surface->views); + wl_list_init(&sub->surface->views); + + surface_stash_subsurface_views(sub->surface); } +} + +static void +surface_free_unused_subsurface_views(struct weston_surface *surface) +{ + struct weston_subsurface *sub; + struct weston_view *view, *nv; wl_list_for_each(sub, &surface->subsurface_list, parent_link) { - if (!weston_surface_is_mapped(sub->surface)) + if (sub->surface == surface) continue; - if (sub->surface == surface) { - weston_surface_update_transform(sub->surface); - wl_list_insert(compositor->surface_list.prev, - &sub->surface->link); - } else { - surface_list_add(compositor, sub->surface); + wl_list_for_each_safe(view, nv, &sub->unused_views, surface_link) + weston_view_destroy(view); + + surface_free_unused_subsurface_views(sub->surface); + } +} + +static void +view_list_add_subsurface_view(struct weston_compositor *compositor, + struct weston_subsurface *sub, + struct weston_view *parent) +{ + struct weston_subsurface *child; + struct weston_view *view = NULL, *iv; + + wl_list_for_each(iv, &sub->unused_views, surface_link) { + if (iv->geometry.parent == parent) { + view = iv; + break; } } + + if (view) { + /* Put it back in the surface's list of views */ + wl_list_remove(&view->surface_link); + wl_list_insert(&sub->surface->views, &view->surface_link); + } else { + view = weston_view_create(sub->surface); + weston_view_set_position(view, + sub->position.x, + sub->position.y); + weston_view_set_transform_parent(view, parent); + } + + weston_view_update_transform(view); + + if (wl_list_empty(&sub->surface->subsurface_list)) { + wl_list_insert(compositor->view_list.prev, &view->link); + return; + } + + wl_list_for_each(child, &sub->surface->subsurface_list, parent_link) { + if (child->surface == sub->surface) + wl_list_insert(compositor->view_list.prev, &view->link); + else + view_list_add_subsurface_view(compositor, child, view); + } } static void -weston_compositor_build_surface_list(struct weston_compositor *compositor) +view_list_add(struct weston_compositor *compositor, + struct weston_view *view) { - struct weston_surface *surface; + struct weston_subsurface *sub; + + weston_view_update_transform(view); + + if (wl_list_empty(&view->surface->subsurface_list)) { + wl_list_insert(compositor->view_list.prev, &view->link); + return; + } + + wl_list_for_each(sub, &view->surface->subsurface_list, parent_link) { + if (sub->surface == view->surface) + wl_list_insert(compositor->view_list.prev, &view->link); + else + view_list_add_subsurface_view(compositor, sub, view); + } +} + +static void +weston_compositor_build_view_list(struct weston_compositor *compositor) +{ + struct weston_view *view; struct weston_layer *layer; - wl_list_init(&compositor->surface_list); + wl_list_for_each(layer, &compositor->layer_list, link) + wl_list_for_each(view, &layer->view_list, layer_link) + surface_stash_subsurface_views(view->surface); + + wl_list_init(&compositor->view_list); wl_list_for_each(layer, &compositor->layer_list, link) { - wl_list_for_each(surface, &layer->surface_list, layer_link) { - surface_list_add(compositor, surface); + wl_list_for_each(view, &layer->view_list, layer_link) { + view_list_add(compositor, view); } } + + wl_list_for_each(layer, &compositor->layer_list, link) + wl_list_for_each(view, &layer->view_list, layer_link) + surface_free_unused_subsurface_views(view->surface); } static int weston_output_repaint(struct weston_output *output, uint32_t msecs) { struct weston_compositor *ec = output->compositor; - struct weston_surface *es; + struct weston_view *ev; struct weston_animation *animation, *next; struct weston_frame_callback *cb, *cnext; struct wl_list frame_callback_list; pixman_region32_t output_damage; int r; + if (output->destroying) + return 0; + /* Rebuild the surface list and update surface transforms up front. */ - weston_compositor_build_surface_list(ec); + weston_compositor_build_view_list(ec); if (output->assign_planes && !output->disable_planes) output->assign_planes(output); else - wl_list_for_each(es, &ec->surface_list, link) - weston_surface_move_to_plane(es, &ec->primary_plane); + wl_list_for_each(ev, &ec->view_list, link) + weston_view_move_to_plane(ev, &ec->primary_plane); wl_list_init(&frame_callback_list); - wl_list_for_each(es, &ec->surface_list, link) { - if (es->output == output) { + wl_list_for_each(ev, &ec->view_list, link) { + /* Note: This operation is safe to do multiple times on the + * same surface. + */ + if (ev->surface->output == output) { wl_list_insert_list(&frame_callback_list, - &es->frame_callback_list); - wl_list_init(&es->frame_callback_list); + &ev->surface->frame_callback_list); + wl_list_init(&ev->surface->frame_callback_list); } } @@ -1478,7 +1928,7 @@ idle_repaint(void *data) WL_EXPORT void weston_layer_init(struct weston_layer *layer, struct wl_list *below) { - wl_list_init(&layer->surface_list); + wl_list_init(&layer->view_list); if (below != NULL) wl_list_insert(below, &layer->link); } @@ -1651,29 +2101,23 @@ weston_surface_commit_subsurface_order(struct weston_surface *surface) static void weston_surface_commit(struct weston_surface *surface) { + struct weston_view *view; pixman_region32_t opaque; - int surface_width = 0; - int surface_height = 0; /* wl_surface.set_buffer_transform */ - surface->buffer_transform = surface->pending.buffer_transform; - /* wl_surface.set_buffer_scale */ - surface->buffer_scale = surface->pending.buffer_scale; + /* wl_viewport.set */ + surface->buffer_viewport = surface->pending.buffer_viewport; /* wl_surface.attach */ if (surface->pending.buffer || surface->pending.newly_attached) weston_surface_attach(surface, surface->pending.buffer); - if (surface->buffer_ref.buffer) { - surface_width = weston_surface_buffer_width(surface); - surface_height = weston_surface_buffer_height(surface); - } + weston_surface_set_size_from_buffer(surface); if (surface->configure && surface->pending.newly_attached) surface->configure(surface, - surface->pending.sx, surface->pending.sy, - surface_width, surface_height); + surface->pending.sx, surface->pending.sy); if (surface->pending.buffer) wl_list_remove(&surface->pending.buffer_destroy_listener.link); @@ -1687,20 +2131,21 @@ weston_surface_commit(struct weston_surface *surface) &surface->pending.damage); pixman_region32_intersect_rect(&surface->damage, &surface->damage, 0, 0, - surface->geometry.width, - surface->geometry.height); + surface->width, + surface->height); empty_region(&surface->pending.damage); /* wl_surface.set_opaque_region */ pixman_region32_init_rect(&opaque, 0, 0, - surface->geometry.width, - surface->geometry.height); + surface->width, + surface->height); pixman_region32_intersect(&opaque, &opaque, &surface->pending.opaque); if (!pixman_region32_equal(&opaque, &surface->opaque)) { pixman_region32_copy(&surface->opaque, &opaque); - weston_surface_geometry_dirty(surface); + wl_list_for_each(view, &surface->views, surface_link) + weston_view_geometry_dirty(view); } pixman_region32_fini(&opaque); @@ -1708,8 +2153,8 @@ weston_surface_commit(struct weston_surface *surface) /* wl_surface.set_input_region */ pixman_region32_fini(&surface->input); pixman_region32_init_rect(&surface->input, 0, 0, - surface->geometry.width, - surface->geometry.height); + surface->width, + surface->height); pixman_region32_intersect(&surface->input, &surface->input, &surface->pending.input); @@ -1755,7 +2200,7 @@ surface_set_buffer_transform(struct wl_client *client, { struct weston_surface *surface = wl_resource_get_user_data(resource); - surface->pending.buffer_transform = transform; + surface->pending.buffer_viewport.transform = transform; } static void @@ -1765,7 +2210,7 @@ surface_set_buffer_scale(struct wl_client *client, { struct weston_surface *surface = wl_resource_get_user_data(resource); - surface->pending.buffer_scale = scale; + surface->pending.buffer_viewport.scale = scale; } static const struct wl_surface_interface surface_interface = { @@ -1882,29 +2327,23 @@ static void weston_subsurface_commit_from_cache(struct weston_subsurface *sub) { struct weston_surface *surface = sub->surface; + struct weston_view *view; pixman_region32_t opaque; - int surface_width = 0; - int surface_height = 0; /* wl_surface.set_buffer_transform */ - surface->buffer_transform = sub->cached.buffer_transform; - /* wl_surface.set_buffer_scale */ - surface->buffer_scale = sub->cached.buffer_scale; + /* wl_viewport.set */ + surface->buffer_viewport = sub->cached.buffer_viewport; /* wl_surface.attach */ if (sub->cached.buffer_ref.buffer || sub->cached.newly_attached) weston_surface_attach(surface, sub->cached.buffer_ref.buffer); weston_buffer_reference(&sub->cached.buffer_ref, NULL); - if (surface->buffer_ref.buffer) { - surface_width = weston_surface_buffer_width(surface); - surface_height = weston_surface_buffer_height(surface); - } + weston_surface_set_size_from_buffer(surface); if (surface->configure && sub->cached.newly_attached) - surface->configure(surface, sub->cached.sx, sub->cached.sy, - surface_width, surface_height); + surface->configure(surface, sub->cached.sx, sub->cached.sy); sub->cached.sx = 0; sub->cached.sy = 0; sub->cached.newly_attached = 0; @@ -1914,20 +2353,21 @@ weston_subsurface_commit_from_cache(struct weston_subsurface *sub) &sub->cached.damage); pixman_region32_intersect_rect(&surface->damage, &surface->damage, 0, 0, - surface->geometry.width, - surface->geometry.height); + surface->width, + surface->height); empty_region(&sub->cached.damage); /* wl_surface.set_opaque_region */ pixman_region32_init_rect(&opaque, 0, 0, - surface->geometry.width, - surface->geometry.height); + surface->width, + surface->height); pixman_region32_intersect(&opaque, &opaque, &sub->cached.opaque); if (!pixman_region32_equal(&opaque, &surface->opaque)) { pixman_region32_copy(&surface->opaque, &opaque); - weston_surface_geometry_dirty(surface); + wl_list_for_each(view, &surface->views, surface_link) + weston_view_geometry_dirty(view); } pixman_region32_fini(&opaque); @@ -1935,8 +2375,8 @@ weston_subsurface_commit_from_cache(struct weston_subsurface *sub) /* wl_surface.set_input_region */ pixman_region32_fini(&surface->input); pixman_region32_init_rect(&surface->input, 0, 0, - surface->geometry.width, - surface->geometry.height); + surface->width, + surface->height); pixman_region32_intersect(&surface->input, &surface->input, &sub->cached.input); @@ -1980,8 +2420,7 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub) surface->pending.sy = 0; surface->pending.newly_attached = 0; - sub->cached.buffer_transform = surface->pending.buffer_transform; - sub->cached.buffer_scale = surface->pending.buffer_scale; + sub->cached.buffer_viewport = surface->pending.buffer_viewport; pixman_region32_copy(&sub->cached.opaque, &surface->pending.opaque); @@ -2060,9 +2499,13 @@ static void weston_subsurface_parent_commit(struct weston_subsurface *sub, int parent_is_synchronized) { + struct weston_view *view; if (sub->position.set) { - weston_surface_set_position(sub->surface, - sub->position.x, sub->position.y); + wl_list_for_each(view, &sub->surface->views, surface_link) + weston_view_set_position(view, + sub->position.x, + sub->position.y); + sub->position.set = 0; } @@ -2071,23 +2514,21 @@ weston_subsurface_parent_commit(struct weston_subsurface *sub, } static void -subsurface_configure(struct weston_surface *surface, int32_t dx, int32_t dy, - int32_t width, int32_t height) +subsurface_configure(struct weston_surface *surface, int32_t dx, int32_t dy) { struct weston_compositor *compositor = surface->compositor; + struct weston_view *view; - weston_surface_configure(surface, - surface->geometry.x + dx, - surface->geometry.y + dy, - width, height); + wl_list_for_each(view, &surface->views, surface_link) + weston_view_set_position(view, + view->geometry.x + dx, + view->geometry.y + dy); /* No need to check parent mappedness, because if parent is not * mapped, parent is not in a visible layer, so this sub-surface * will not be drawn either. */ if (!weston_surface_is_mapped(surface)) { - wl_list_init(&surface->layer_link); - /* Cannot call weston_surface_update_transform(), * because that would call it also for the parent surface, * which might not be mapped yet. That would lead to @@ -2359,6 +2800,8 @@ weston_subsurface_link_surface(struct weston_subsurface *sub, static void weston_subsurface_destroy(struct weston_subsurface *sub) { + struct weston_view *view, *next; + assert(sub->surface); if (sub->resource) { @@ -2366,7 +2809,9 @@ weston_subsurface_destroy(struct weston_subsurface *sub) assert(sub->parent_destroy_listener.notify == subsurface_handle_parent_destroy); - weston_surface_set_transform_parent(sub->surface, NULL); + wl_list_for_each_safe(view, next, &sub->surface->views, surface_link) + weston_view_destroy(view); + if (sub->parent) weston_subsurface_unlink_parent(sub); @@ -2405,6 +2850,8 @@ weston_subsurface_create(uint32_t id, struct weston_surface *surface, if (!sub) return NULL; + wl_list_init(&sub->unused_views); + sub->resource = wl_resource_create(client, &wl_subsurface_interface, 1, id); if (!sub->resource) { @@ -2419,7 +2866,6 @@ weston_subsurface_create(uint32_t id, struct weston_surface *surface, weston_subsurface_link_parent(sub, parent); weston_subsurface_cache_init(sub); sub->synchronized = 1; - weston_surface_set_transform_parent(surface, parent); return sub; } @@ -2631,14 +3077,14 @@ weston_plane_init(struct weston_plane *plane, WL_EXPORT void weston_plane_release(struct weston_plane *plane) { - struct weston_surface *surface; + struct weston_view *view; pixman_region32_fini(&plane->damage); pixman_region32_fini(&plane->clip); - wl_list_for_each(surface, &plane->compositor->surface_list, link) { - if (surface->plane == plane) - surface->plane = NULL; + wl_list_for_each(view, &plane->compositor->view_list, link) { + if (view->plane == plane) + view->plane = NULL; } wl_list_remove(&plane->link); @@ -2702,9 +3148,51 @@ bind_output(struct wl_client *client, wl_output_send_done(resource); } +/* Move other outputs when one is removed so the space remains contiguos. */ +static void +weston_compositor_remove_output(struct weston_compositor *compositor, + struct weston_output *remove_output) +{ + struct weston_output *output; + int offset = 0; + + wl_list_for_each(output, &compositor->output_list, link) { + if (output == remove_output) { + offset = output->width; + continue; + } + + if (offset > 0) { + weston_output_move(output, + output->x - offset, output->y); + output->dirty = 1; + } + } +} + +static void +weston_compositor_verify_pointers(struct weston_compositor *ec) +{ + struct weston_seat *seat; + + wl_list_for_each(seat, &ec->seat_list, link) { + if (!seat->pointer) + continue; + + weston_pointer_verify(seat->pointer); + } +} + WL_EXPORT void weston_output_destroy(struct weston_output *output) { + output->destroying = 1; + + weston_compositor_remove_output(output->compositor, output); + wl_list_remove(&output->link); + + weston_compositor_verify_pointers(output->compositor); + wl_signal_emit(&output->destroy_signal, output); free(output->name); @@ -2782,12 +3270,12 @@ weston_output_update_matrix(struct weston_output *output) weston_matrix_init(&output->matrix); weston_matrix_translate(&output->matrix, - -(output->x + (output->border.right + output->width - output->border.left) / 2.0), - -(output->y + (output->border.bottom + output->height - output->border.top) / 2.0), 0); + -(output->x + output->width / 2.0), + -(output->y + output->height / 2.0), 0); weston_matrix_scale(&output->matrix, - 2.0 / (output->width + output->border.left + output->border.right), - -2.0 / (output->height + output->border.top + output->border.bottom), 1); + 2.0 / output->width, + -2.0 / output->height, 1); weston_output_compute_transform(output); @@ -2795,7 +3283,7 @@ weston_output_update_matrix(struct weston_output *output) magnification = 1 / (1 - output->zoom.spring_z.current); weston_matrix_init(&camera); weston_matrix_init(&modelview); - weston_output_update_zoom(output, output->zoom.type); + weston_output_update_zoom(output); weston_matrix_translate(&camera, output->zoom.trans_x, -output->zoom.trans_y, 0); weston_matrix_invert(&modelview, &camera); @@ -2836,8 +3324,8 @@ weston_output_transform_scale_init(struct weston_output *output, uint32_t transf output->height /= scale; } -WL_EXPORT void -weston_output_move(struct weston_output *output, int x, int y) +static void +weston_output_init_geometry(struct weston_output *output, int x, int y) { output->x = x; output->y = y; @@ -2848,6 +3336,41 @@ weston_output_move(struct weston_output *output, int x, int y) output->height); } +WL_EXPORT void +weston_output_move(struct weston_output *output, int x, int y) +{ + pixman_region32_t old_region; + struct wl_resource *resource; + + output->move_x = x - output->x; + output->move_y = y - output->y; + + if (output->move_x == 0 && output->move_y == 0) + return; + + pixman_region32_init(&old_region); + pixman_region32_copy(&old_region, &output->region); + + weston_output_init_geometry(output, x, y); + + output->dirty = 1; + + /* Move views on this output. */ + wl_signal_emit(&output->move_signal, output); + + /* Notify clients of the change for output position. */ + wl_resource_for_each(resource, &output->resource_list) + wl_output_send_geometry(resource, + output->x, + output->y, + output->mm_width, + output->mm_height, + output->subpixel, + output->make, + output->model, + output->transform); +} + WL_EXPORT void weston_output_init(struct weston_output *output, struct weston_compositor *c, int x, int y, int mm_width, int mm_height, uint32_t transform, @@ -2856,10 +3379,6 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c, output->compositor = c; output->x = x; output->y = y; - output->border.top = 0; - output->border.bottom = 0; - output->border.left = 0; - output->border.right = 0; output->mm_width = mm_width; output->mm_height = mm_height; output->dirty = 1; @@ -2868,11 +3387,12 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c, weston_output_transform_scale_init(output, transform, scale); weston_output_init_zoom(output); - weston_output_move(output, x, y); + weston_output_init_geometry(output, x, y); weston_output_damage(output); wl_signal_init(&output->frame_signal); wl_signal_init(&output->destroy_signal); + wl_signal_init(&output->move_signal); wl_list_init(&output->animation_list); wl_list_init(&output->resource_list); @@ -2887,7 +3407,7 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c, WL_EXPORT void weston_output_transform_coordinate(struct weston_output *output, - int device_x, int device_y, + wl_fixed_t device_x, wl_fixed_t device_y, wl_fixed_t *x, wl_fixed_t *y) { wl_fixed_t tx, ty; @@ -2899,36 +3419,36 @@ weston_output_transform_coordinate(struct weston_output *output, switch(output->transform) { case WL_OUTPUT_TRANSFORM_NORMAL: default: - tx = wl_fixed_from_int(device_x); - ty = wl_fixed_from_int(device_y); + tx = device_x; + ty = device_y; break; case WL_OUTPUT_TRANSFORM_90: - tx = wl_fixed_from_int(device_y); - ty = height - wl_fixed_from_int(device_x); + tx = device_y; + ty = height - device_x; break; case WL_OUTPUT_TRANSFORM_180: - tx = width - wl_fixed_from_int(device_x); - ty = height - wl_fixed_from_int(device_y); + tx = width - device_x; + ty = height - device_y; break; case WL_OUTPUT_TRANSFORM_270: - tx = width - wl_fixed_from_int(device_y); - ty = wl_fixed_from_int(device_x); + tx = width - device_y; + ty = device_x; break; case WL_OUTPUT_TRANSFORM_FLIPPED: - tx = width - wl_fixed_from_int(device_x); - ty = wl_fixed_from_int(device_y); + tx = width - device_x; + ty = device_y; break; case WL_OUTPUT_TRANSFORM_FLIPPED_90: - tx = width - wl_fixed_from_int(device_y); - ty = height - wl_fixed_from_int(device_x); + tx = width - device_y; + ty = height - device_x; break; case WL_OUTPUT_TRANSFORM_FLIPPED_180: - tx = wl_fixed_from_int(device_x); - ty = height - wl_fixed_from_int(device_y); + tx = device_x; + ty = height - device_y; break; case WL_OUTPUT_TRANSFORM_FLIPPED_270: - tx = wl_fixed_from_int(device_y); - ty = wl_fixed_from_int(device_x); + tx = device_y; + ty = device_x; break; } @@ -2936,6 +3456,129 @@ weston_output_transform_coordinate(struct weston_output *output, *y = ty / output->current_scale + wl_fixed_from_int(output->y); } +static void +destroy_viewport(struct wl_resource *resource) +{ + struct weston_surface *surface = + wl_resource_get_user_data(resource); + + surface->viewport_resource = NULL; + surface->pending.buffer_viewport.viewport_set = 0; +} + +static void +viewport_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +viewport_set(struct wl_client *client, + struct wl_resource *resource, + wl_fixed_t src_x, + wl_fixed_t src_y, + wl_fixed_t src_width, + wl_fixed_t src_height, + int32_t dst_width, + int32_t dst_height) +{ + struct weston_surface *surface = + wl_resource_get_user_data(resource); + + assert(surface->viewport_resource != NULL); + + if (wl_fixed_to_double(src_width) < 0 || + wl_fixed_to_double(src_height) < 0) { + wl_resource_post_error(resource, + WL_VIEWPORT_ERROR_BAD_VALUE, + "source dimensions must be non-negative (%fx%f)", + wl_fixed_to_double(src_width), + wl_fixed_to_double(src_height)); + return; + } + + if (dst_width <= 0 || dst_height <= 0) { + wl_resource_post_error(resource, + WL_VIEWPORT_ERROR_BAD_VALUE, + "destination dimensions must be positive (%dx%d)", + dst_width, dst_height); + return; + } + + surface->pending.buffer_viewport.viewport_set = 1; + + surface->pending.buffer_viewport.src_x = src_x; + surface->pending.buffer_viewport.src_y = src_y; + surface->pending.buffer_viewport.src_width = src_width; + surface->pending.buffer_viewport.src_height = src_height; + surface->pending.buffer_viewport.dst_width = dst_width; + surface->pending.buffer_viewport.dst_height = dst_height; +} + +static const struct wl_viewport_interface viewport_interface = { + viewport_destroy, + viewport_set +}; + +static void +scaler_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +scaler_get_viewport(struct wl_client *client, + struct wl_resource *scaler, + uint32_t id, + struct wl_resource *surface_resource) +{ + struct weston_surface *surface = wl_resource_get_user_data(surface_resource); + struct wl_resource *resource; + + if (surface->viewport_resource) { + wl_resource_post_error(scaler, + WL_SCALER_ERROR_VIEWPORT_EXISTS, + "a viewport for that surface already exists"); + return; + } + + resource = wl_resource_create(client, &wl_viewport_interface, + 1, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &viewport_interface, + surface, destroy_viewport); + + surface->viewport_resource = resource; +} + +static const struct wl_scaler_interface scaler_interface = { + scaler_destroy, + scaler_get_viewport +}; + +static void +bind_scaler(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create(client, &wl_scaler_interface, + 1, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &scaler_interface, + NULL, NULL); +} + static void compositor_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) @@ -3024,12 +3667,17 @@ weston_compositor_init(struct weston_compositor *ec, ec, bind_subcompositor)) return -1; - wl_list_init(&ec->surface_list); + if (!wl_global_create(ec->wl_display, &wl_scaler_interface, 1, + ec, bind_scaler)) + return -1; + + wl_list_init(&ec->view_list); wl_list_init(&ec->plane_list); wl_list_init(&ec->layer_list); wl_list_init(&ec->seat_list); wl_list_init(&ec->output_list); wl_list_init(&ec->key_binding_list); + wl_list_init(&ec->modifier_binding_list); wl_list_init(&ec->button_binding_list); wl_list_init(&ec->touch_binding_list); wl_list_init(&ec->axis_binding_list); @@ -3056,7 +3704,6 @@ weston_compositor_init(struct weston_compositor *ec, ec->ping_handler = NULL; screenshooter_create(ec); - text_cursor_position_notifier_create(ec); text_backend_init(ec); wl_data_device_manager_init(ec->wl_display); @@ -3090,6 +3737,9 @@ weston_compositor_shutdown(struct weston_compositor *ec) wl_list_for_each_safe(output, next, &ec->output_list, link) output->destroy(output); + if (ec->renderer) + ec->renderer->destroy(ec); + weston_binding_list_destroy_all(&ec->key_binding_list); weston_binding_list_destroy_all(&ec->button_binding_list); weston_binding_list_destroy_all(&ec->touch_binding_list); @@ -3103,6 +3753,21 @@ weston_compositor_shutdown(struct weston_compositor *ec) weston_config_destroy(ec->config); } +WL_EXPORT void +weston_compositor_set_default_pointer_grab(struct weston_compositor *ec, + const struct weston_pointer_grab_interface *interface) +{ + struct weston_seat *seat; + + ec->default_pointer_grab = interface; + wl_list_for_each(seat, &ec->seat_list, link) { + if (seat->pointer) { + weston_pointer_set_default_grab(seat->pointer, + interface); + } + } +} + WL_EXPORT void weston_version(int *major, int *minor, int *micro) { @@ -3243,8 +3908,8 @@ on_caught_signal(int s, siginfo_t *siginfo, void *context) raise(SIGTRAP); } -static void * -load_module(const char *name, const char *entrypoint) +WL_EXPORT void * +weston_load_module(const char *name, const char *entrypoint) { char path[PATH_MAX]; void *module, *init; @@ -3294,7 +3959,7 @@ load_modules(struct weston_compositor *ec, const char *modules, while (*p) { end = strchrnul(p, ','); snprintf(buffer, sizeof buffer, "%.*s", (int) (end - p), p); - module_init = load_module(buffer, "module_init"); + module_init = weston_load_module(buffer, "module_init"); if (module_init) module_init(ec, argc, argv); p = end; @@ -3397,6 +4062,10 @@ usage(int error_code) "Options for wayland-backend.so:\n\n" " --width=WIDTH\t\tWidth of Wayland surface\n" " --height=HEIGHT\tHeight of Wayland surface\n" + " --scale=SCALE\tScale factor of ouput\n" + " --fullscreen\t\tRun in fullscreen mode\n" + " --use-pixman\t\tUse the pixman (CPU) renderer\n" + " --output-count=COUNT\tCreate multiple outputs\n" " --display=DISPLAY\tWayland display to connect to\n\n"); #if defined(BUILD_RPI_COMPOSITOR) && defined(HAVE_BCM_HOST) @@ -3406,6 +4075,8 @@ usage(int error_code) " --single-buffer\tUse single-buffered Dispmanx elements.\n" " --transform=TR\tThe output transformation, TR is one of:\n" "\tnormal 90 180 270 flipped flipped-90 flipped-180 flipped-270\n" + " --opaque-regions\tEnable support for opaque regions, can be " + "very slow without support in the GPU firmware.\n" "\n"); #endif @@ -3452,6 +4123,7 @@ int main(int argc, char *argv[]) struct weston_config *config); int i; char *backend = NULL; + char *option_backend = NULL; char *shell = NULL; char *modules, *option_modules = NULL; char *log = NULL; @@ -3463,7 +4135,7 @@ int main(int argc, char *argv[]) struct weston_config_section *section; const struct weston_option core_options[] = { - { WESTON_OPTION_STRING, "backend", 'B', &backend }, + { WESTON_OPTION_STRING, "backend", 'B', &option_backend }, { WESTON_OPTION_STRING, "shell", 0, &shell }, { WESTON_OPTION_STRING, "socket", 'S', &socket_name }, { WESTON_OPTION_INTEGER, "idle-time", 'i', &idle_time }, @@ -3509,6 +4181,19 @@ int main(int argc, char *argv[]) signals[3] = wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler, NULL); + config = weston_config_parse("weston.ini"); + if (config != NULL) { + weston_log("Using config file '%s'\n", + weston_config_get_full_path(config)); + } else { + weston_log("Starting with no config file.\n"); + } + section = weston_config_get_section(config, "core", NULL, NULL); + + weston_config_section_get_string(section, "backend", &backend, NULL); + if (option_backend) { + backend = option_backend; + } if (!backend) { if (getenv("WAYLAND_DISPLAY")) backend = "wayland-backend.so"; @@ -3518,17 +4203,9 @@ int main(int argc, char *argv[]) backend = WESTON_NATIVE_BACKEND; } - config = weston_config_parse("weston.ini"); - if (config != NULL) { - weston_log("Using config file '%s'\n", - weston_config_get_full_path(config)); - } else { - weston_log("Starting with no config file.\n"); - } - section = weston_config_get_section(config, "core", NULL, NULL); weston_config_section_get_string(section, "modules", &modules, ""); - backend_init = load_module(backend, "backend_init"); + backend_init = weston_load_module(backend, "backend_init"); if (!backend_init) exit(EXIT_FAILURE); @@ -3542,6 +4219,7 @@ int main(int argc, char *argv[]) segv_compositor = ec; ec->idle_time = idle_time; + ec->default_pointer_grab = NULL; setenv("WAYLAND_DISPLAY", socket_name, 1); diff --git a/src/compositor.h b/src/compositor.h index 0dcb604e..22a485fc 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -92,6 +92,8 @@ struct weston_shell_interface { struct shell_surface *(*create_shell_surface)(void *shell, struct weston_surface *surface, const struct weston_shell_client *client); + struct weston_view *(*get_primary_view)(void *shell, + struct shell_surface *shsurf); void (*set_toplevel)(struct shell_surface *shsurf); @@ -112,10 +114,6 @@ struct weston_shell_interface { }; -struct weston_border { - int32_t left, right, top, bottom; -}; - struct weston_animation { void (*frame)(struct weston_animation *animation, struct weston_output *output, uint32_t msecs); @@ -140,18 +138,12 @@ struct weston_spring { uint32_t clip; }; -enum { - ZOOM_FOCUS_POINTER, - ZOOM_FOCUS_TEXT -}; - struct weston_fixed_point { wl_fixed_t x, y; }; struct weston_output_zoom { int active; - uint32_t type; float increment; float level; float max_level; @@ -163,7 +155,7 @@ struct weston_output_zoom { struct weston_fixed_point from; struct weston_fixed_point to; struct weston_fixed_point current; - struct weston_fixed_point text_cursor; + struct wl_listener motion_listener; }; /* bit compatible with drm definitions. */ @@ -194,7 +186,6 @@ struct weston_output { struct wl_list animation_list; int32_t x, y, width, height; int32_t mm_width, mm_height; - struct weston_border border; pixman_region32_t region; pixman_region32_t previous_damage; int repaint_needed; @@ -203,8 +194,11 @@ struct weston_output { int dirty; struct wl_signal frame_signal; struct wl_signal destroy_signal; + struct wl_signal move_signal; + int move_x, move_y; uint32_t frame_time; int disable_planes; + int destroying; char *make, *model, *serial_number; uint32_t subpixel; @@ -242,7 +236,8 @@ struct weston_output { struct weston_pointer_grab; struct weston_pointer_grab_interface { void (*focus)(struct weston_pointer_grab *grab); - void (*motion)(struct weston_pointer_grab *grab, uint32_t time); + void (*motion)(struct weston_pointer_grab *grab, uint32_t time, + wl_fixed_t x, wl_fixed_t y); void (*button)(struct weston_pointer_grab *grab, uint32_t time, uint32_t button, uint32_t state); void (*cancel)(struct weston_pointer_grab *grab); @@ -314,11 +309,14 @@ struct weston_pointer { struct wl_list resource_list; struct wl_list focus_resource_list; - struct weston_surface *focus; + struct weston_view *focus; uint32_t focus_serial; + struct wl_listener focus_view_listener; + struct wl_listener focus_resource_listener; struct wl_signal focus_signal; + struct wl_signal motion_signal; - struct weston_surface *sprite; + struct weston_view *sprite; struct wl_listener sprite_destroy_listener; int32_t hotspot_x, hotspot_y; @@ -339,10 +337,14 @@ struct weston_touch { struct wl_list resource_list; struct wl_list focus_resource_list; - struct weston_surface *focus; + struct weston_view *focus; + struct wl_listener focus_view_listener; + struct wl_listener focus_resource_listener; uint32_t focus_serial; struct wl_signal focus_signal; + uint32_t num_tp; + struct weston_touch_grab *grab; struct weston_touch_grab default_grab; int grab_touch_id; @@ -352,12 +354,12 @@ struct weston_touch { }; struct weston_pointer * -weston_pointer_create(void); +weston_pointer_create(struct weston_seat *seat); void weston_pointer_destroy(struct weston_pointer *pointer); void weston_pointer_set_focus(struct weston_pointer *pointer, - struct weston_surface *surface, + struct weston_view *view, wl_fixed_t sx, wl_fixed_t sy); void weston_pointer_start_grab(struct weston_pointer *pointer, @@ -367,6 +369,14 @@ weston_pointer_end_grab(struct weston_pointer *pointer); void weston_pointer_clamp(struct weston_pointer *pointer, wl_fixed_t *fx, wl_fixed_t *fy); +void +weston_pointer_move(struct weston_pointer *pointer, + wl_fixed_t x, wl_fixed_t y); +void +weston_pointer_set_default_grab(struct weston_pointer *pointer, + const struct weston_pointer_grab_interface *interface); +void +weston_pointer_verify(struct weston_pointer *pointer); struct weston_keyboard * weston_keyboard_create(void); @@ -387,7 +397,7 @@ void weston_touch_destroy(struct weston_touch *touch); void weston_touch_set_focus(struct weston_seat *seat, - struct weston_surface *surface); + struct weston_view *view); void weston_touch_start_grab(struct weston_touch *device, struct weston_touch_grab *grab); @@ -405,10 +415,15 @@ void weston_seat_set_selection(struct weston_seat *seat, struct weston_data_source *source, uint32_t serial); int -weston_seat_start_drag(struct weston_seat *seat, +weston_pointer_start_drag(struct weston_pointer *pointer, struct weston_data_source *source, struct weston_surface *icon, struct wl_client *client); +int +weston_touch_start_drag(struct weston_touch *touch, + struct weston_data_source *source, + struct weston_surface *icon, + struct wl_client *client); struct weston_xkb_info { struct xkb_keymap *keymap; @@ -435,6 +450,7 @@ struct weston_keyboard { struct wl_list resource_list; struct wl_list focus_resource_list; struct weston_surface *focus; + struct wl_listener focus_resource_listener; uint32_t focus_serial; struct wl_signal focus_signal; @@ -455,6 +471,13 @@ struct weston_keyboard { struct weston_keyboard_grab input_method_grab; struct wl_resource *input_method_resource; + + struct weston_xkb_info *xkb_info; + struct { + struct xkb_state *state; + enum weston_led leds; + } xkb_state; + struct xkb_keymap *pending_keymap; }; struct weston_seat { @@ -484,16 +507,9 @@ struct weston_seat { struct wl_listener selection_data_source_listener; struct wl_signal selection_signal; - uint32_t num_tp; - void (*led_update)(struct weston_seat *ws, enum weston_led leds); - struct weston_xkb_info *xkb_info; - struct { - struct xkb_state *state; - enum weston_led leds; - } xkb_state; - + uint32_t slot_map; struct input_method *input_method; char *seat_name; }; @@ -507,7 +523,7 @@ enum { }; struct weston_layer { - struct wl_list surface_list; + struct wl_list view_list; struct wl_list link; }; @@ -528,11 +544,9 @@ struct weston_renderer { pixman_region32_t *output_damage); void (*flush_damage)(struct weston_surface *surface); void (*attach)(struct weston_surface *es, struct weston_buffer *buffer); - int (*create_surface)(struct weston_surface *surface); void (*surface_set_color)(struct weston_surface *surface, float red, float green, float blue, float alpha); - void (*destroy_surface)(struct weston_surface *surface); void (*destroy)(struct weston_compositor *ec); }; @@ -578,9 +592,10 @@ struct weston_compositor { struct wl_list output_list; struct wl_list seat_list; struct wl_list layer_list; - struct wl_list surface_list; + struct wl_list view_list; struct wl_list plane_list; struct wl_list key_binding_list; + struct wl_list modifier_binding_list; struct wl_list button_binding_list; struct wl_list touch_binding_list; struct wl_list axis_binding_list; @@ -591,12 +606,12 @@ struct weston_compositor { uint32_t idle_inhibit; int idle_time; /* timeout, s */ + const struct weston_pointer_grab_interface *default_pointer_grab; + /* Repaint state. */ struct weston_plane primary_plane; uint32_t capabilities; /* combination of enum weston_capability */ - uint32_t focus; - struct weston_renderer *renderer; pixman_format_code_t read_format; @@ -638,6 +653,23 @@ struct weston_buffer_reference { struct wl_listener destroy_listener; }; +struct weston_buffer_viewport { + /* wl_surface.set_buffer_transform */ + uint32_t transform; + + /* wl_surface.set_scaling_factor */ + int32_t scale; + + /* bool for whether wl_viewport.set has been + * called yet (before this is called there is no + * cropping or scaling on the surface) */ + int viewport_set; /* bool */ + + wl_fixed_t src_x, src_y; + wl_fixed_t src_width, src_height; + int32_t dst_width, dst_height; +}; + struct weston_region { struct wl_resource *resource; pixman_region32_t region; @@ -684,58 +716,57 @@ struct weston_subsurface { struct wl_list frame_callback_list; /* wl_surface.set_buffer_transform */ - uint32_t buffer_transform; - /* wl_surface.set_buffer_scale */ - int32_t buffer_scale; + struct weston_buffer_viewport buffer_viewport; } cached; int synchronized; + + /* Used for constructing the view tree */ + struct wl_list unused_views; }; -/* Using weston_surface transformations +/* Using weston_view transformations * - * To add a transformation to a surface, create a struct weston_transform, and - * add it to the list surface->geometry.transformation_list. Whenever you - * change the list, anything under surface->geometry, or anything in the + * To add a transformation to a view, create a struct weston_transform, and + * add it to the list view->geometry.transformation_list. Whenever you + * change the list, anything under view->geometry, or anything in the * weston_transforms linked into the list, you must call - * weston_surface_geometry_dirty(). + * weston_view_geometry_dirty(). * * The order in the list defines the order of transformations. Let the list * contain the transformation matrices M1, ..., Mn as head to tail. The - * transformation is applied to surface-local coordinate vector p as + * transformation is applied to view-local coordinate vector p as * P = Mn * ... * M2 * M1 * p * to produce the global coordinate vector P. The total transform * Mn * ... * M2 * M1 - * is cached in surface->transform.matrix, and the inverse of it in - * surface->transform.inverse. + * is cached in view->transform.matrix, and the inverse of it in + * view->transform.inverse. * - * The list always contains surface->transform.position transformation, which - * is the translation by surface->geometry.x and y. + * The list always contains view->transform.position transformation, which + * is the translation by view->geometry.x and y. * * If you want to apply a transformation in local coordinates, add your * weston_transform to the head of the list. If you want to apply a * transformation in global coordinates, add it to the tail of the list. * - * If surface->geometry.parent is set, the total transformation of this - * surface will be the parent's total transformation and this transformation + * If view->geometry.parent is set, the total transformation of this + * view will be the parent's total transformation and this transformation * combined: * Mparent * Mn * ... * M2 * M1 */ -struct weston_surface { - struct wl_resource *resource; +struct weston_view { + struct weston_surface *surface; + struct wl_list surface_link; struct wl_signal destroy_signal; - struct weston_compositor *compositor; - pixman_region32_t clip; - pixman_region32_t damage; - pixman_region32_t opaque; /* part of geometry, see below */ - pixman_region32_t input; + struct wl_list link; struct wl_list layer_link; - float alpha; /* part of geometry, see below */ struct weston_plane *plane; - int32_t ref_count; + + pixman_region32_t clip; + float alpha; /* part of geometry, see below */ void *renderer_state; @@ -745,20 +776,19 @@ struct weston_surface { */ struct { float x, y; /* surface translation on display */ - int32_t width, height; /* struct weston_transform */ struct wl_list transformation_list; /* managed by weston_surface_set_transform_parent() */ - struct weston_surface *parent; + struct weston_view *parent; struct wl_listener parent_destroy_listener; struct wl_list child_list; /* geometry.parent_link */ struct wl_list parent_link; } geometry; /* State derived from geometry state, read-only. - * This is updated by weston_surface_update_transform(). + * This is updated by weston_view_update_transform(). */ struct { int dirty; @@ -789,13 +819,51 @@ struct weston_surface { */ uint32_t output_mask; + struct wl_listener output_move_listener; + struct wl_listener output_destroy_listener; +}; + +struct weston_surface { + struct wl_resource *resource; + struct wl_signal destroy_signal; + struct weston_compositor *compositor; + pixman_region32_t damage; + pixman_region32_t opaque; /* part of geometry, see below */ + pixman_region32_t input; + int32_t width, height; + int32_t ref_count; + + /* Not for long-term storage. This exists for book-keeping while + * iterating over surfaces and views + */ + int32_t touched; + + void *renderer_state; + + struct wl_list views; + + /* + * Which output to vsync this surface to. + * Used to determine, whether to send or queue frame events. + * Must be NULL, if 'link' is not in weston_compositor::surface_list. + */ + struct weston_output *output; + + /* + * A more complete representation of all outputs this surface is + * displayed on. + */ + uint32_t output_mask; + struct wl_list frame_callback_list; struct weston_buffer_reference buffer_ref; - uint32_t buffer_transform; - int32_t buffer_scale; + struct weston_buffer_viewport buffer_viewport; int keep_buffer; /* bool for backends to prevent early release */ + /* wl_viewport resource for this surface */ + struct wl_resource *viewport_resource; + /* All the pending state, that wl_surface.commit will apply. */ struct { /* wl_surface.attach */ @@ -818,10 +886,9 @@ struct weston_surface { struct wl_list frame_callback_list; /* wl_surface.set_buffer_transform */ - uint32_t buffer_transform; - /* wl_surface.set_scaling_factor */ - int32_t buffer_scale; + /* wl_viewport.set */ + struct weston_buffer_viewport buffer_viewport; } pending; /* @@ -829,9 +896,15 @@ struct weston_surface { * a new buffer has been set up for this surface. The integer params * are the sx and sy paramerters supplied to surface::attach . */ - void (*configure)(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height); + void (*configure)(struct weston_surface *es, int32_t sx, int32_t sy); void *configure_private; + /* If non-NULL, this function will be called on surface->output:: + * destroy, after the output is removed from the compositor's + * output list and the remaining outputs moved. + */ + void (*output_destroyed)(struct weston_surface *surface); + /* Parent's list of its sub-surfaces, weston_subsurface:parent_link. * Contains also the parent itself as a dummy weston_subsurface, * if the list is not empty. @@ -849,41 +922,36 @@ void weston_version(int *major, int *minor, int *micro); void -weston_surface_update_transform(struct weston_surface *surface); +weston_view_update_transform(struct weston_view *view); void -weston_surface_geometry_dirty(struct weston_surface *surface); +weston_view_geometry_dirty(struct weston_view *view); void -weston_surface_to_global_fixed(struct weston_surface *surface, - wl_fixed_t sx, wl_fixed_t sy, - wl_fixed_t *x, wl_fixed_t *y); +weston_view_to_global_fixed(struct weston_view *view, + wl_fixed_t sx, wl_fixed_t sy, + wl_fixed_t *x, wl_fixed_t *y); void -weston_surface_to_global_float(struct weston_surface *surface, - float sx, float sy, float *x, float *y); +weston_view_to_global_float(struct weston_view *view, + float sx, float sy, float *x, float *y); void -weston_surface_from_global_float(struct weston_surface *surface, - float x, float y, float *sx, float *sy); +weston_view_from_global_float(struct weston_view *view, + float x, float y, float *vx, float *vy); void -weston_surface_from_global(struct weston_surface *surface, - int32_t x, int32_t y, int32_t *sx, int32_t *sy); +weston_view_from_global(struct weston_view *view, + int32_t x, int32_t y, int32_t *vx, int32_t *vy); void -weston_surface_from_global_fixed(struct weston_surface *surface, - wl_fixed_t x, wl_fixed_t y, - wl_fixed_t *sx, wl_fixed_t *sy); -int32_t -weston_surface_buffer_width(struct weston_surface *surface); -int32_t -weston_surface_buffer_height(struct weston_surface *surface); +weston_view_from_global_fixed(struct weston_view *view, + wl_fixed_t x, wl_fixed_t y, + wl_fixed_t *vx, wl_fixed_t *vy); WL_EXPORT void weston_surface_to_buffer_float(struct weston_surface *surface, float x, float y, float *bx, float *by); WL_EXPORT void weston_surface_to_buffer(struct weston_surface *surface, - int sx, int sy, int *bx, int *by); - + int sx, int sy, int *bx, int *by); pixman_box32_t weston_surface_to_buffer_rect(struct weston_surface *surface, pixman_box32_t rect); @@ -967,10 +1035,10 @@ void weston_compositor_offscreen(struct weston_compositor *compositor); void weston_compositor_sleep(struct weston_compositor *compositor); -struct weston_surface * -weston_compositor_pick_surface(struct weston_compositor *compositor, - wl_fixed_t x, wl_fixed_t y, - wl_fixed_t *sx, wl_fixed_t *sy); +struct weston_view * +weston_compositor_pick_view(struct weston_compositor *compositor, + wl_fixed_t x, wl_fixed_t y, + wl_fixed_t *sx, wl_fixed_t *sy); struct weston_binding; @@ -984,6 +1052,15 @@ weston_compositor_add_key_binding(struct weston_compositor *compositor, weston_key_binding_handler_t binding, void *data); +typedef void (*weston_modifier_binding_handler_t)(struct weston_seat *seat, + enum weston_keyboard_modifier modifier, + void *data); +struct weston_binding * +weston_compositor_add_modifier_binding(struct weston_compositor *compositor, + enum weston_keyboard_modifier modifier, + weston_modifier_binding_handler_t binding, + void *data); + typedef void (*weston_button_binding_handler_t)(struct weston_seat *seat, uint32_t time, uint32_t button, void *data); @@ -1028,6 +1105,12 @@ weston_compositor_run_key_binding(struct weston_compositor *compositor, struct weston_seat *seat, uint32_t time, uint32_t key, enum wl_keyboard_key_state state); + +void +weston_compositor_run_modifier_binding(struct weston_compositor *compositor, + struct weston_seat *seat, + enum weston_keyboard_modifier modifier, + enum wl_keyboard_key_state state); void weston_compositor_run_button_binding(struct weston_compositor *compositor, struct weston_seat *seat, uint32_t time, @@ -1047,6 +1130,10 @@ weston_compositor_run_debug_binding(struct weston_compositor *compositor, uint32_t key, enum wl_keyboard_key_state state); +void +weston_compositor_set_default_pointer_grab(struct weston_compositor *compositor, + const struct weston_pointer_grab_interface *interface); + int weston_environment_get_fd(const char *env); @@ -1056,24 +1143,33 @@ weston_compositor_top(struct weston_compositor *compositor); struct weston_surface * weston_surface_create(struct weston_compositor *compositor); +struct weston_view * +weston_view_create(struct weston_surface *surface); + void -weston_surface_configure(struct weston_surface *surface, - float x, float y, int width, int height); +weston_view_destroy(struct weston_view *view); void -weston_surface_restack(struct weston_surface *surface, struct wl_list *below); +weston_view_set_position(struct weston_view *view, + float x, float y); void -weston_surface_set_position(struct weston_surface *surface, - float x, float y); +weston_view_set_transform_parent(struct weston_view *view, + struct weston_view *parent); + +int +weston_view_is_mapped(struct weston_view *view); void -weston_surface_set_transform_parent(struct weston_surface *surface, - struct weston_surface *parent); +weston_view_schedule_repaint(struct weston_view *view); int weston_surface_is_mapped(struct weston_surface *surface); +WL_EXPORT void +weston_surface_set_size(struct weston_surface *surface, + int32_t width, int32_t height); + void weston_surface_schedule_repaint(struct weston_surface *surface); @@ -1081,11 +1177,14 @@ void weston_surface_damage(struct weston_surface *surface); void -weston_surface_damage_below(struct weston_surface *surface); +weston_view_damage_below(struct weston_view *view); void -weston_surface_move_to_plane(struct weston_surface *surface, - struct weston_plane *plane); +weston_view_move_to_plane(struct weston_view *view, + struct weston_plane *plane); +void +weston_view_unmap(struct weston_view *view); + void weston_surface_unmap(struct weston_surface *surface); @@ -1108,12 +1207,11 @@ weston_compositor_init(struct weston_compositor *ec, struct wl_display *display, void weston_compositor_shutdown(struct weston_compositor *ec); void -weston_text_cursor_position_notify(struct weston_surface *surface, - wl_fixed_t x, wl_fixed_t y); -void weston_output_init_zoom(struct weston_output *output); void -weston_output_update_zoom(struct weston_output *output, uint32_t type); +weston_output_update_zoom(struct weston_output *output); +void +weston_output_activate_zoom(struct weston_output *output); void weston_output_update_matrix(struct weston_output *output); void @@ -1124,8 +1222,8 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c, void weston_output_destroy(struct weston_output *output); void -weston_output_transform_coordinate(struct weston_output *x11_output, - int device_x, int device_y, +weston_output_transform_coordinate(struct weston_output *output, + wl_fixed_t device_x, wl_fixed_t device_y, wl_fixed_t *x, wl_fixed_t *y); void @@ -1145,6 +1243,8 @@ void weston_seat_release_touch(struct weston_seat *seat); void weston_seat_repick(struct weston_seat *seat); +void +weston_seat_update_keymap(struct weston_seat *seat, struct xkb_keymap *keymap); void weston_seat_release(struct weston_seat *seat); @@ -1195,9 +1295,6 @@ screenshooter_create(struct weston_compositor *ec); struct clipboard * clipboard_create(struct weston_seat *seat); -void -text_cursor_position_notifier_create(struct weston_compositor *ec); - int text_backend_init(struct weston_compositor *ec); @@ -1220,23 +1317,37 @@ weston_client_launch(struct weston_compositor *compositor, void weston_watch_process(struct weston_process *process); -struct weston_surface_animation; -typedef void (*weston_surface_animation_done_func_t)(struct weston_surface_animation *animation, void *data); +struct weston_view_animation; +typedef void (*weston_view_animation_done_func_t)(struct weston_view_animation *animation, void *data); -struct weston_surface_animation * -weston_zoom_run(struct weston_surface *surface, float start, float stop, - weston_surface_animation_done_func_t done, void *data); +void +weston_view_animation_destroy(struct weston_view_animation *animation); + +struct weston_view_animation * +weston_zoom_run(struct weston_view *view, float start, float stop, + weston_view_animation_done_func_t done, void *data); -struct weston_surface_animation * -weston_fade_run(struct weston_surface *surface, +struct weston_view_animation * +weston_fade_run(struct weston_view *view, float start, float end, float k, - weston_surface_animation_done_func_t done, void *data); + weston_view_animation_done_func_t done, void *data); + +struct weston_view_animation * +weston_move_scale_run(struct weston_view *view, int dx, int dy, + float start, float end, int reverse, + weston_view_animation_done_func_t done, void *data); + void -weston_fade_update(struct weston_surface_animation *fade, float target); +weston_fade_update(struct weston_view_animation *fade, float target); -struct weston_surface_animation * -weston_slide_run(struct weston_surface *surface, float start, float stop, - weston_surface_animation_done_func_t done, void *data); +struct weston_view_animation * +weston_stable_fade_run(struct weston_view *front_view, float start, + struct weston_view *back_view, float end, + weston_view_animation_done_func_t done, void *data); + +struct weston_view_animation * +weston_slide_run(struct weston_view *view, float start, float stop, + weston_view_animation_done_func_t done, void *data); void weston_surface_set_color(struct weston_surface *surface, @@ -1270,6 +1381,14 @@ weston_transformed_rect(int width, int height, enum wl_output_transform transform, int32_t scale, pixman_box32_t rect); +void +weston_transformed_region(int width, int height, + enum wl_output_transform transform, + int32_t scale, + pixman_region32_t *src, pixman_region32_t *dest); + +void * +weston_load_module(const char *name, const char *entrypoint); #ifdef __cplusplus } diff --git a/src/data-device.c b/src/data-device.c index c3dc0bda..483e22e9 100644 --- a/src/data-device.c +++ b/src/data-device.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "compositor.h" @@ -33,15 +34,24 @@ struct weston_drag { struct wl_client *client; struct weston_data_source *data_source; struct wl_listener data_source_listener; - struct weston_surface *focus; + struct weston_view *focus; struct wl_resource *focus_resource; struct wl_listener focus_listener; - struct weston_pointer_grab grab; - struct weston_surface *icon; + struct weston_view *icon; struct wl_listener icon_destroy_listener; int32_t dx, dy; }; +struct weston_pointer_drag { + struct weston_drag base; + struct weston_pointer_grab grab; +}; + +struct weston_touch_drag { + struct weston_drag base; + struct weston_touch_grab grab; +}; + static void empty_region(pixman_region32_t *region) { @@ -171,30 +181,67 @@ static struct wl_data_source_interface data_source_interface = { }; static void -drag_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height) +drag_surface_configure(struct weston_drag *drag, + struct weston_pointer *pointer, + struct weston_touch *touch, + struct weston_surface *es, + int32_t sx, int32_t sy) { - struct weston_drag *drag = es->configure_private; - struct weston_pointer *pointer = drag->grab.pointer; struct wl_list *list; float fx, fy; + assert((pointer != NULL && touch == NULL) || + (pointer == NULL && touch != NULL)); + if (!weston_surface_is_mapped(es) && es->buffer_ref.buffer) { - if (pointer->sprite && weston_surface_is_mapped(pointer->sprite)) + if (pointer && pointer->sprite && + weston_view_is_mapped(pointer->sprite)) list = &pointer->sprite->layer_link; else - list = &es->compositor->cursor_layer.surface_list; + list = &es->compositor->cursor_layer.view_list; - wl_list_insert(list, &es->layer_link); - weston_surface_update_transform(es); + wl_list_remove(&drag->icon->layer_link); + wl_list_insert(list, &drag->icon->layer_link); + weston_view_update_transform(drag->icon); empty_region(&es->pending.input); } drag->dx += sx; drag->dy += sy; - fx = wl_fixed_to_double(pointer->x) + drag->dx; - fy = wl_fixed_to_double(pointer->y) + drag->dy; - weston_surface_configure(es, fx, fy, width, height); + /* init to 0 for avoiding a compile warning */ + fx = fy = 0; + if (pointer) { + fx = wl_fixed_to_double(pointer->x) + drag->dx; + fy = wl_fixed_to_double(pointer->y) + drag->dy; + } else if (touch) { + fx = wl_fixed_to_double(touch->grab_x) + drag->dx; + fy = wl_fixed_to_double(touch->grab_y) + drag->dy; + } + weston_view_set_position(drag->icon, fx, fy); +} + +static void +pointer_drag_surface_configure(struct weston_surface *es, + int32_t sx, int32_t sy) +{ + struct weston_pointer_drag *drag = es->configure_private; + struct weston_pointer *pointer = drag->grab.pointer; + + assert(es->configure == pointer_drag_surface_configure); + + drag_surface_configure(&drag->base, pointer, NULL, es, sx, sy); +} + +static void +touch_drag_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy) +{ + struct weston_touch_drag *drag = es->configure_private; + struct weston_touch *touch = drag->grab.touch; + + assert(es->configure == touch_drag_surface_configure); + + drag_surface_configure(&drag->base, NULL, touch, es, sx, sy); } static void @@ -207,14 +254,20 @@ destroy_drag_focus(struct wl_listener *listener, void *data) } static void -weston_drag_set_focus(struct weston_drag *drag, struct weston_surface *surface, - wl_fixed_t sx, wl_fixed_t sy) +weston_drag_set_focus(struct weston_drag *drag, + struct weston_seat *seat, + struct weston_view *view, + wl_fixed_t sx, wl_fixed_t sy) { - struct weston_pointer *pointer = drag->grab.pointer; struct wl_resource *resource, *offer = NULL; - struct wl_display *display = pointer->seat->compositor->wl_display; + struct wl_display *display = seat->compositor->wl_display; uint32_t serial; + if (drag->focus && view && drag->focus->surface == view->surface) { + drag->focus = view; + return; + } + if (drag->focus_resource) { wl_data_device_send_leave(drag->focus_resource); wl_list_remove(&drag->focus_listener.link); @@ -222,15 +275,15 @@ weston_drag_set_focus(struct weston_drag *drag, struct weston_surface *surface, drag->focus = NULL; } - if (!surface) + if (!view || !view->surface->resource) return; if (!drag->data_source && - wl_resource_get_client(surface->resource) != drag->client) + wl_resource_get_client(view->surface->resource) != drag->client) return; - resource = wl_resource_find_for_client(&pointer->seat->drag_resource_list, - wl_resource_get_client(surface->resource)); + resource = wl_resource_find_for_client(&seat->drag_resource_list, + wl_resource_get_client(view->surface->resource)); if (!resource) return; @@ -243,10 +296,10 @@ weston_drag_set_focus(struct weston_drag *drag, struct weston_surface *surface, return; } - wl_data_device_send_enter(resource, serial, surface->resource, + wl_data_device_send_enter(resource, serial, view->surface->resource, sx, sy, offer); - drag->focus = surface; + drag->focus = view; drag->focus_listener.notify = destroy_drag_focus; wl_resource_add_destroy_listener(resource, &drag->focus_listener); drag->focus_resource = resource; @@ -255,60 +308,71 @@ weston_drag_set_focus(struct weston_drag *drag, struct weston_surface *surface, static void drag_grab_focus(struct weston_pointer_grab *grab) { - struct weston_drag *drag = - container_of(grab, struct weston_drag, grab); + struct weston_pointer_drag *drag = + container_of(grab, struct weston_pointer_drag, grab); struct weston_pointer *pointer = grab->pointer; - struct weston_surface *surface; + struct weston_view *view; wl_fixed_t sx, sy; - surface = weston_compositor_pick_surface(pointer->seat->compositor, - pointer->x, pointer->y, - &sx, &sy); - if (drag->focus != surface) - weston_drag_set_focus(drag, surface, sx, sy); + view = weston_compositor_pick_view(pointer->seat->compositor, + pointer->x, pointer->y, + &sx, &sy); + if (drag->base.focus != view) + weston_drag_set_focus(&drag->base, pointer->seat, view, sx, sy); } static void -drag_grab_motion(struct weston_pointer_grab *grab, uint32_t time) +drag_grab_motion(struct weston_pointer_grab *grab, uint32_t time, + wl_fixed_t x, wl_fixed_t y) { - struct weston_drag *drag = - container_of(grab, struct weston_drag, grab); + struct weston_pointer_drag *drag = + container_of(grab, struct weston_pointer_drag, grab); struct weston_pointer *pointer = drag->grab.pointer; float fx, fy; wl_fixed_t sx, sy; - if (drag->icon) { - fx = wl_fixed_to_double(pointer->x) + drag->dx; - fy = wl_fixed_to_double(pointer->y) + drag->dy; - weston_surface_set_position(drag->icon, fx, fy); - weston_surface_schedule_repaint(drag->icon); + weston_pointer_move(pointer, x, y); + + if (drag->base.icon) { + fx = wl_fixed_to_double(pointer->x) + drag->base.dx; + fy = wl_fixed_to_double(pointer->y) + drag->base.dy; + weston_view_set_position(drag->base.icon, fx, fy); + weston_view_schedule_repaint(drag->base.icon); } - if (drag->focus_resource) { - weston_surface_from_global_fixed(drag->focus, - pointer->x, pointer->y, - &sx, &sy); + if (drag->base.focus_resource) { + weston_view_from_global_fixed(drag->base.focus, + pointer->x, pointer->y, + &sx, &sy); - wl_data_device_send_motion(drag->focus_resource, time, sx, sy); + wl_data_device_send_motion(drag->base.focus_resource, time, sx, sy); } } static void -data_device_end_drag_grab(struct weston_drag *drag) +data_device_end_drag_grab(struct weston_drag *drag, + struct weston_seat *seat) { if (drag->icon) { - if (weston_surface_is_mapped(drag->icon)) - weston_surface_unmap(drag->icon); + if (weston_view_is_mapped(drag->icon)) + weston_view_unmap(drag->icon); - drag->icon->configure = NULL; - empty_region(&drag->icon->pending.input); + drag->icon->surface->configure = NULL; + empty_region(&drag->icon->surface->pending.input); wl_list_remove(&drag->icon_destroy_listener.link); + weston_view_destroy(drag->icon); } - weston_drag_set_focus(drag, NULL, 0, 0); + weston_drag_set_focus(drag, seat, NULL, 0, 0); +} - weston_pointer_end_grab(drag->grab.pointer); +static void +data_device_end_pointer_drag_grab(struct weston_pointer_drag *drag) +{ + struct weston_pointer *pointer = drag->grab.pointer; + data_device_end_drag_grab(&drag->base, pointer->seat); + weston_pointer_end_grab(pointer); free(drag); } @@ -316,37 +380,37 @@ static void drag_grab_button(struct weston_pointer_grab *grab, uint32_t time, uint32_t button, uint32_t state_w) { - struct weston_drag *drag = - container_of(grab, struct weston_drag, grab); + struct weston_pointer_drag *drag = + container_of(grab, struct weston_pointer_drag, grab); struct weston_pointer *pointer = drag->grab.pointer; enum wl_pointer_button_state state = state_w; - if (drag->focus_resource && + if (drag->base.focus_resource && pointer->grab_button == button && state == WL_POINTER_BUTTON_STATE_RELEASED) - wl_data_device_send_drop(drag->focus_resource); + wl_data_device_send_drop(drag->base.focus_resource); if (pointer->button_count == 0 && state == WL_POINTER_BUTTON_STATE_RELEASED) { - if (drag->data_source) - wl_list_remove(&drag->data_source_listener.link); - data_device_end_drag_grab(drag); + if (drag->base.data_source) + wl_list_remove(&drag->base.data_source_listener.link); + data_device_end_pointer_drag_grab(drag); } } static void drag_grab_cancel(struct weston_pointer_grab *grab) { - struct weston_drag *drag = - container_of(grab, struct weston_drag, grab); + struct weston_pointer_drag *drag = + container_of(grab, struct weston_pointer_drag, grab); - if (drag->data_source) - wl_list_remove(&drag->data_source_listener.link); + if (drag->base.data_source) + wl_list_remove(&drag->base.data_source_listener.link); - data_device_end_drag_grab(drag); + data_device_end_pointer_drag_grab(drag); } -static const struct weston_pointer_grab_interface drag_grab_interface = { +static const struct weston_pointer_grab_interface pointer_drag_grab_interface = { drag_grab_focus, drag_grab_motion, drag_grab_button, @@ -354,12 +418,109 @@ static const struct weston_pointer_grab_interface drag_grab_interface = { }; static void -destroy_data_device_source(struct wl_listener *listener, void *data) +drag_grab_touch_down(struct weston_touch_grab *grab, uint32_t time, + int touch_id, wl_fixed_t sx, wl_fixed_t sy) { - struct weston_drag *drag = container_of(listener, struct weston_drag, - data_source_listener); +} + +static void +data_device_end_touch_drag_grab(struct weston_touch_drag *drag) +{ + struct weston_touch *touch = drag->grab.touch; + + data_device_end_drag_grab(&drag->base, touch->seat); + weston_touch_end_grab(touch); + free(drag); +} + +static void +drag_grab_touch_up(struct weston_touch_grab *grab, + uint32_t time, int touch_id) +{ + struct weston_touch_drag *touch_drag = + container_of(grab, struct weston_touch_drag, grab); + struct weston_touch *touch = grab->touch; + + if (touch_id != touch->grab_touch_id) + return; + + if (touch_drag->base.focus_resource) + wl_data_device_send_drop(touch_drag->base.focus_resource); + if (touch_drag->base.data_source) + wl_list_remove(&touch_drag->base.data_source_listener.link); + data_device_end_touch_drag_grab(touch_drag); +} + +static void +drag_grab_touch_focus(struct weston_touch_drag *drag) +{ + struct weston_touch *touch = drag->grab.touch; + struct weston_view *view; + wl_fixed_t view_x, view_y; - data_device_end_drag_grab(drag); + view = weston_compositor_pick_view(touch->seat->compositor, + touch->grab_x, touch->grab_y, + &view_x, &view_y); + if (drag->base.focus != view) + weston_drag_set_focus(&drag->base, touch->seat, + view, view_x, view_y); +} + +static void +drag_grab_touch_motion(struct weston_touch_grab *grab, uint32_t time, + int touch_id, wl_fixed_t sx, wl_fixed_t sy) +{ + struct weston_touch_drag *touch_drag = + container_of(grab, struct weston_touch_drag, grab); + struct weston_touch *touch = grab->touch; + wl_fixed_t view_x, view_y; + float fx, fy; + + if (touch_id != touch->grab_touch_id) + return; + + drag_grab_touch_focus(touch_drag); + if (touch_drag->base.icon) { + fx = wl_fixed_to_double(touch->grab_x) + touch_drag->base.dx; + fy = wl_fixed_to_double(touch->grab_y) + touch_drag->base.dy; + weston_view_set_position(touch_drag->base.icon, fx, fy); + weston_view_schedule_repaint(touch_drag->base.icon); + } + + if (touch_drag->base.focus_resource) { + weston_view_from_global_fixed(touch_drag->base.focus, + touch->grab_x, touch->grab_y, + &view_x, &view_y); + wl_data_device_send_motion(touch_drag->base.focus_resource, time, + view_x, view_y); + } +} + +static void +drag_grab_touch_cancel(struct weston_touch_grab *grab) +{ + struct weston_touch_drag *touch_drag = + container_of(grab, struct weston_touch_drag, grab); + + if (touch_drag->base.data_source) + wl_list_remove(&touch_drag->base.data_source_listener.link); + data_device_end_touch_drag_grab(touch_drag); +} + +static const struct weston_touch_grab_interface touch_drag_grab_interface = { + drag_grab_touch_down, + drag_grab_touch_up, + drag_grab_touch_motion, + drag_grab_touch_cancel +}; + +static void +destroy_pointer_data_device_source(struct wl_listener *listener, void *data) +{ + struct weston_pointer_drag *drag = container_of(listener, + struct weston_pointer_drag, base.data_source_listener); + + data_device_end_pointer_drag_grab(drag); } static void @@ -372,40 +533,102 @@ handle_drag_icon_destroy(struct wl_listener *listener, void *data) } WL_EXPORT int -weston_seat_start_drag(struct weston_seat *seat, +weston_pointer_start_drag(struct weston_pointer *pointer, struct weston_data_source *source, struct weston_surface *icon, struct wl_client *client) { - struct weston_drag *drag; + struct weston_pointer_drag *drag; drag = zalloc(sizeof *drag); if (drag == NULL) return -1; - drag->grab.interface = &drag_grab_interface; - drag->client = client; - drag->data_source = source; - drag->icon = icon; + drag->grab.interface = &pointer_drag_grab_interface; + drag->base.client = client; + drag->base.data_source = source; + + if (icon) { + drag->base.icon = weston_view_create(icon); + if (drag->base.icon == NULL) { + free(drag); + return -1; + } + + drag->base.icon_destroy_listener.notify = handle_drag_icon_destroy; + wl_signal_add(&icon->destroy_signal, + &drag->base.icon_destroy_listener); + + icon->configure = pointer_drag_surface_configure; + icon->configure_private = drag; + } else { + drag->base.icon = NULL; + } if (source) { - drag->data_source_listener.notify = destroy_data_device_source; + drag->base.data_source_listener.notify = destroy_pointer_data_device_source; wl_signal_add(&source->destroy_signal, - &drag->data_source_listener); + &drag->base.data_source_listener); } + weston_pointer_set_focus(pointer, NULL, + wl_fixed_from_int(0), wl_fixed_from_int(0)); + weston_pointer_start_grab(pointer, &drag->grab); + + return 0; +} + +static void +destroy_touch_data_device_source(struct wl_listener *listener, void *data) +{ + struct weston_touch_drag *drag = container_of(listener, + struct weston_touch_drag, base.data_source_listener); + + data_device_end_touch_drag_grab(drag); +} + +WL_EXPORT int +weston_touch_start_drag(struct weston_touch *touch, + struct weston_data_source *source, + struct weston_surface *icon, + struct wl_client *client) +{ + struct weston_touch_drag *drag; + + drag = zalloc(sizeof *drag); + if (drag == NULL) + return -1; + + drag->grab.interface = &touch_drag_grab_interface; + drag->base.client = client; + drag->base.data_source = source; + if (icon) { - drag->icon_destroy_listener.notify = handle_drag_icon_destroy; + drag->base.icon = weston_view_create(icon); + if (drag->base.icon == NULL) { + free(drag); + return -1; + } + + drag->base.icon_destroy_listener.notify = handle_drag_icon_destroy; wl_signal_add(&icon->destroy_signal, - &drag->icon_destroy_listener); + &drag->base.icon_destroy_listener); - icon->configure = drag_surface_configure; + icon->configure = touch_drag_surface_configure; icon->configure_private = drag; + } else { + drag->base.icon = NULL; } - weston_pointer_set_focus(seat->pointer, NULL, - wl_fixed_from_int(0), wl_fixed_from_int(0)); - weston_pointer_start_grab(seat->pointer, &drag->grab); + if (source) { + drag->base.data_source_listener.notify = destroy_touch_data_device_source; + wl_signal_add(&source->destroy_signal, + &drag->base.data_source_listener); + } + + weston_touch_start_grab(touch, &drag->grab); + + drag_grab_touch_focus(drag); return 0; } @@ -419,10 +642,15 @@ data_device_start_drag(struct wl_client *client, struct wl_resource *resource, struct weston_seat *seat = wl_resource_get_user_data(resource); struct weston_data_source *source = NULL; struct weston_surface *icon = NULL; + int32_t ret = 0; - if (seat->pointer->button_count == 0 || + if ((seat->pointer->button_count == 0 || seat->pointer->grab_serial != serial || - seat->pointer->focus != wl_resource_get_user_data(origin_resource)) + !seat->pointer->focus || + seat->pointer->focus->surface != wl_resource_get_user_data(origin_resource)) && + (seat->touch->grab_serial != serial || + !seat->touch->focus || + seat->touch->focus->surface != wl_resource_get_user_data(origin_resource))) return; /* FIXME: Check that the data source type array isn't empty. */ @@ -438,7 +666,17 @@ data_device_start_drag(struct wl_client *client, struct wl_resource *resource, return; } - if (weston_seat_start_drag(seat, source, icon, client) < 0) + if (seat->pointer->button_count == 1 && + seat->pointer->grab_serial == serial && + seat->pointer->focus && + seat->pointer->focus->surface == wl_resource_get_user_data(origin_resource)) + ret = weston_pointer_start_drag(seat->pointer, source, icon, client); + else if (seat->touch->grab_serial != serial || + seat->touch->focus || + seat->touch->focus->surface != wl_resource_get_user_data(origin_resource)) + ret = weston_touch_start_drag(seat->touch, source, icon, client); + + if (ret < 0) wl_resource_post_no_memory(resource); } diff --git a/src/dbus.c b/src/dbus.c new file mode 100644 index 00000000..a1abbd54 --- /dev/null +++ b/src/dbus.c @@ -0,0 +1,404 @@ +/* + * Copyright © 2013 David Herrmann + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * DBus Helpers + * This file contains the dbus mainloop integration and several helpers to + * make lowlevel libdbus access easier. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compositor.h" +#include "dbus.h" + +/* + * DBus Mainloop Integration + * weston_dbus_bind() and weston_dbus_unbind() allow to bind an existing + * DBusConnection to an existing wl_event_loop object. All dbus dispatching + * is then nicely integrated into the wayland event loop. + * Note that this only provides basic watch and timeout dispatching. No + * remote thread wakeup, signal handling or other dbus insanity is supported. + * This is fine as long as you don't use any of the deprecated libdbus + * interfaces (like waking up remote threads..). There is really no rational + * reason to support these. + */ + +static int weston_dbus_dispatch_watch(int fd, uint32_t mask, void *data) +{ + DBusWatch *watch = data; + uint32_t flags = 0; + + if (dbus_watch_get_enabled(watch)) { + if (mask & WL_EVENT_READABLE) + flags |= DBUS_WATCH_READABLE; + if (mask & WL_EVENT_WRITABLE) + flags |= DBUS_WATCH_WRITABLE; + if (mask & WL_EVENT_HANGUP) + flags |= DBUS_WATCH_HANGUP; + if (mask & WL_EVENT_ERROR) + flags |= DBUS_WATCH_ERROR; + + dbus_watch_handle(watch, flags); + } + + return 0; +} + +static dbus_bool_t weston_dbus_add_watch(DBusWatch *watch, void *data) +{ + struct wl_event_loop *loop = data; + struct wl_event_source *s; + int fd; + uint32_t mask = 0, flags; + + if (dbus_watch_get_enabled(watch)) { + flags = dbus_watch_get_flags(watch); + if (flags & DBUS_WATCH_READABLE) + mask |= WL_EVENT_READABLE; + if (flags & DBUS_WATCH_WRITABLE) + mask |= WL_EVENT_WRITABLE; + } + + fd = dbus_watch_get_unix_fd(watch); + s = wl_event_loop_add_fd(loop, fd, mask, weston_dbus_dispatch_watch, + watch); + if (!s) + return FALSE; + + dbus_watch_set_data(watch, s, NULL); + return TRUE; +} + +static void weston_dbus_remove_watch(DBusWatch *watch, void *data) +{ + struct wl_event_source *s; + + s = dbus_watch_get_data(watch); + if (!s) + return; + + wl_event_source_remove(s); +} + +static void weston_dbus_toggle_watch(DBusWatch *watch, void *data) +{ + struct wl_event_source *s; + uint32_t mask = 0, flags; + + s = dbus_watch_get_data(watch); + if (!s) + return; + + if (dbus_watch_get_enabled(watch)) { + flags = dbus_watch_get_flags(watch); + if (flags & DBUS_WATCH_READABLE) + mask |= WL_EVENT_READABLE; + if (flags & DBUS_WATCH_WRITABLE) + mask |= WL_EVENT_WRITABLE; + } + + wl_event_source_fd_update(s, mask); +} + +static int weston_dbus_dispatch_timeout(void *data) +{ + DBusTimeout *timeout = data; + + if (dbus_timeout_get_enabled(timeout)) + dbus_timeout_handle(timeout); + + return 0; +} + +static int weston_dbus_adjust_timeout(DBusTimeout *timeout, + struct wl_event_source *s) +{ + int64_t t = 0; + + if (dbus_timeout_get_enabled(timeout)) + t = dbus_timeout_get_interval(timeout); + + return wl_event_source_timer_update(s, t); +} + +static dbus_bool_t weston_dbus_add_timeout(DBusTimeout *timeout, void *data) +{ + struct wl_event_loop *loop = data; + struct wl_event_source *s; + int r; + + s = wl_event_loop_add_timer(loop, weston_dbus_dispatch_timeout, + timeout); + if (!s) + return FALSE; + + r = weston_dbus_adjust_timeout(timeout, s); + if (r < 0) { + wl_event_source_remove(s); + return FALSE; + } + + dbus_timeout_set_data(timeout, s, NULL); + return TRUE; +} + +static void weston_dbus_remove_timeout(DBusTimeout *timeout, void *data) +{ + struct wl_event_source *s; + + s = dbus_timeout_get_data(timeout); + if (!s) + return; + + wl_event_source_remove(s); +} + +static void weston_dbus_toggle_timeout(DBusTimeout *timeout, void *data) +{ + struct wl_event_source *s; + + s = dbus_timeout_get_data(timeout); + if (!s) + return; + + weston_dbus_adjust_timeout(timeout, s); +} + +static int weston_dbus_dispatch(int fd, uint32_t mask, void *data) +{ + DBusConnection *c = data; + int r; + + do { + r = dbus_connection_dispatch(c); + if (r == DBUS_DISPATCH_COMPLETE) + r = 0; + else if (r == DBUS_DISPATCH_DATA_REMAINS) + r = -EAGAIN; + else if (r == DBUS_DISPATCH_NEED_MEMORY) + r = -ENOMEM; + else + r = -EIO; + } while (r == -EAGAIN); + + if (r) + weston_log("cannot dispatch dbus events: %d\n", r); + + return 0; +} + +static int weston_dbus_bind(struct wl_event_loop *loop, DBusConnection *c, + struct wl_event_source **ctx_out) +{ + bool b; + int r, fd; + + /* Idle events cannot reschedule themselves, therefore we use a dummy + * event-fd and mark it for post-dispatch. Hence, the dbus + * dispatcher is called after every dispatch-round. + * This is required as dbus doesn't allow dispatching events from + * within its own event sources. */ + fd = eventfd(0, EFD_CLOEXEC); + if (fd < 0) + return -errno; + + *ctx_out = wl_event_loop_add_fd(loop, fd, 0, weston_dbus_dispatch, c); + close(fd); + + if (!*ctx_out) + return -ENOMEM; + + wl_event_source_check(*ctx_out); + + b = dbus_connection_set_watch_functions(c, + weston_dbus_add_watch, + weston_dbus_remove_watch, + weston_dbus_toggle_watch, + loop, + NULL); + if (!b) { + r = -ENOMEM; + goto error; + } + + b = dbus_connection_set_timeout_functions(c, + weston_dbus_add_timeout, + weston_dbus_remove_timeout, + weston_dbus_toggle_timeout, + loop, + NULL); + if (!b) { + r = -ENOMEM; + goto error; + } + + dbus_connection_ref(c); + return 0; + +error: + dbus_connection_set_timeout_functions(c, NULL, NULL, NULL, + NULL, NULL); + dbus_connection_set_watch_functions(c, NULL, NULL, NULL, + NULL, NULL); + wl_event_source_remove(*ctx_out); + *ctx_out = NULL; + return r; +} + +static void weston_dbus_unbind(DBusConnection *c, struct wl_event_source *ctx) +{ + dbus_connection_set_timeout_functions(c, NULL, NULL, NULL, + NULL, NULL); + dbus_connection_set_watch_functions(c, NULL, NULL, NULL, + NULL, NULL); + dbus_connection_unref(c); + wl_event_source_remove(ctx); +} + +/* + * Convenience Helpers + * Several convenience helpers are provided to make using dbus in weston + * easier. We don't use any of the gdbus or qdbus helpers as they pull in + * huge dependencies and actually are quite awful to use. Instead, we only + * use the basic low-level libdbus library. + */ + +int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus, + DBusConnection **out, struct wl_event_source **ctx_out) +{ + DBusConnection *c; + int r; + + /* Ihhh, global state.. stupid dbus. */ + dbus_connection_set_change_sigpipe(FALSE); + + /* This is actually synchronous. It blocks for some authentication and + * setup. We just trust the dbus-server here and accept this blocking + * call. There is no real reason to complicate things further and make + * this asynchronous/non-blocking. A context should be created during + * thead/process/app setup, so blocking calls should be fine. */ + c = dbus_bus_get_private(bus, NULL); + if (!c) + return -EIO; + + dbus_connection_set_exit_on_disconnect(c, FALSE); + + r = weston_dbus_bind(loop, c, ctx_out); + if (r < 0) + goto error; + + *out = c; + return r; + +error: + dbus_connection_close(c); + dbus_connection_unref(c); + return r; +} + +void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx) +{ + weston_dbus_unbind(c, ctx); + dbus_connection_close(c); + dbus_connection_unref(c); +} + +int weston_dbus_add_match(DBusConnection *c, const char *format, ...) +{ + DBusError err; + int r; + va_list list; + char *str; + + va_start(list, format); + r = vasprintf(&str, format, list); + va_end(list); + + if (r < 0) + return -ENOMEM; + + dbus_error_init(&err); + dbus_bus_add_match(c, str, &err); + free(str); + if (dbus_error_is_set(&err)) { + dbus_error_free(&err); + return -EIO; + } + + return 0; +} + +int weston_dbus_add_match_signal(DBusConnection *c, const char *sender, + const char *iface, const char *member, + const char *path) +{ + return weston_dbus_add_match(c, + "type='signal'," + "sender='%s'," + "interface='%s'," + "member='%s'," + "path='%s'", + sender, iface, member, path); +} + +void weston_dbus_remove_match(DBusConnection *c, const char *format, ...) +{ + int r; + va_list list; + char *str; + + va_start(list, format); + r = vasprintf(&str, format, list); + va_end(list); + + if (r < 0) + return; + + dbus_bus_remove_match(c, str, NULL); + free(str); +} + +void weston_dbus_remove_match_signal(DBusConnection *c, const char *sender, + const char *iface, const char *member, + const char *path) +{ + return weston_dbus_remove_match(c, + "type='signal'," + "sender='%s'," + "interface='%s'," + "member='%s'," + "path='%s'", + sender, iface, member, path); +} diff --git a/src/dbus.h b/src/dbus.h new file mode 100644 index 00000000..06fe7621 --- /dev/null +++ b/src/dbus.h @@ -0,0 +1,107 @@ +/* + * Copyright © 2013 David Herrmann + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WESTON_DBUS_H_ +#define _WESTON_DBUS_H_ + +#include "config.h" + +#include +#include + +#include "compositor.h" + +#ifdef HAVE_DBUS + +#include + +/* + * weston_dbus_open() - Open new dbus connection + * + * Opens a new dbus connection to the bus given as @bus. It automatically + * integrates the new connection into the main-loop @loop. The connection + * itself is returned in @out. + * This also returns a context source used for dbus dispatching. It is + * returned on success in @ctx_out and must be passed to weston_dbus_close() + * unchanged. You must not access it from outside of a dbus helper! + * + * Returns 0 on success, negative error code on failure. + */ +int weston_dbus_open(struct wl_event_loop *loop, DBusBusType bus, + DBusConnection **out, struct wl_event_source **ctx_out); + +/* + * weston_dbus_close() - Close dbus connection + * + * Closes a dbus connection that was previously opened via weston_dbus_open(). + * It unbinds the connection from the main-loop it was previously bound to, + * closes the dbus connection and frees all resources. If you want to access + * @c after this call returns, you must hold a dbus-reference to it. But + * notice that the connection is closed after this returns so it cannot be + * used to spawn new dbus requests. + * You must pass the context source returns by weston_dbus_open() as @ctx. + */ +void weston_dbus_close(DBusConnection *c, struct wl_event_source *ctx); + +/* + * weston_dbus_add_match() - Add dbus match + * + * Configure a dbus-match on the given dbus-connection. This match is saved + * on the dbus-server as long as the connection is open. See dbus-manual + * for information. Compared to the dbus_bus_add_match() this allows a + * var-arg formatted match-string. + */ +int weston_dbus_add_match(DBusConnection *c, const char *format, ...); + +/* + * weston_dbus_add_match_signal() - Add dbus signal match + * + * Same as weston_dbus_add_match() but does the dbus-match formatting for + * signals internally. + */ +int weston_dbus_add_match_signal(DBusConnection *c, const char *sender, + const char *iface, const char *member, + const char *path); + +/* + * weston_dbus_remove_match() - Remove dbus match + * + * Remove a previously configured dbus-match from the dbus server. There is + * no need to remove dbus-matches if you close the connection, anyway. + * Compared to dbus_bus_remove_match() this allows a var-arg formatted + * match string. + */ +void weston_dbus_remove_match(DBusConnection *c, const char *format, ...); + +/* + * weston_dbus_remove_match_signal() - Remove dbus signal match + * + * Same as weston_dbus_remove_match() but does the dbus-match formatting for + * signals internally. + */ +void weston_dbus_remove_match_signal(DBusConnection *c, const char *sender, + const char *iface, const char *member, + const char *path); + +#endif /* HAVE_DBUS */ + +#endif // _WESTON_DBUS_H_ diff --git a/src/evdev.c b/src/evdev.c index eb7631a7..d38f63bd 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -50,7 +50,7 @@ evdev_led_update(struct evdev_device *device, enum weston_led leds) struct input_event ev[ARRAY_LENGTH(map) + 1]; unsigned int i; - if (!device->caps & EVDEV_KEYBOARD) + if (!(device->seat_caps & EVDEV_SEAT_KEYBOARD)) return; memset(ev, 0, sizeof(ev)); @@ -90,10 +90,9 @@ evdev_flush_pending_event(struct evdev_device *device, uint32_t time) struct weston_seat *master = device->seat; wl_fixed_t x, y; int32_t cx, cy; - int slot; + int slot, seat_slot; slot = device->mt.slot; - switch (device->pending_event) { case EVDEV_NONE: return; @@ -104,42 +103,56 @@ evdev_flush_pending_event(struct evdev_device *device, uint32_t time) goto handled; case EVDEV_ABSOLUTE_MT_DOWN: weston_output_transform_coordinate(device->output, - device->mt.slots[slot].x, - device->mt.slots[slot].y, + wl_fixed_from_int(device->mt.slots[slot].x), + wl_fixed_from_int(device->mt.slots[slot].y), &x, &y); - notify_touch(master, time, - slot, x, y, WL_TOUCH_DOWN); + seat_slot = ffs(~master->slot_map) - 1; + device->mt.slots[slot].seat_slot = seat_slot; + master->slot_map |= 1 << seat_slot; + + notify_touch(master, time, seat_slot, x, y, WL_TOUCH_DOWN); goto handled; case EVDEV_ABSOLUTE_MT_MOTION: weston_output_transform_coordinate(device->output, - device->mt.slots[slot].x, - device->mt.slots[slot].y, + wl_fixed_from_int(device->mt.slots[slot].x), + wl_fixed_from_int(device->mt.slots[slot].y), &x, &y); - notify_touch(master, time, - slot, x, y, WL_TOUCH_MOTION); + seat_slot = device->mt.slots[slot].seat_slot; + notify_touch(master, time, seat_slot, x, y, WL_TOUCH_MOTION); goto handled; case EVDEV_ABSOLUTE_MT_UP: - notify_touch(master, time, slot, 0, 0, - WL_TOUCH_UP); + seat_slot = device->mt.slots[slot].seat_slot; + master->slot_map &= ~(1 << seat_slot); + notify_touch(master, time, seat_slot, 0, 0, WL_TOUCH_UP); goto handled; case EVDEV_ABSOLUTE_TOUCH_DOWN: transform_absolute(device, &cx, &cy); weston_output_transform_coordinate(device->output, - cx, cy, &x, &y); - notify_touch(master, time, 0, x, y, WL_TOUCH_DOWN); + wl_fixed_from_int(cx), + wl_fixed_from_int(cy), + &x, &y); + seat_slot = ffs(~master->slot_map) - 1; + device->abs.seat_slot = seat_slot; + master->slot_map |= 1 << seat_slot; + notify_touch(master, time, seat_slot, x, y, WL_TOUCH_DOWN); goto handled; case EVDEV_ABSOLUTE_MOTION: transform_absolute(device, &cx, &cy); weston_output_transform_coordinate(device->output, - cx, cy, &x, &y); + wl_fixed_from_int(cx), + wl_fixed_from_int(cy), + &x, &y); - if (device->caps & EVDEV_TOUCH) - notify_touch(master, time, 0, x, y, WL_TOUCH_MOTION); - else + if (device->seat_caps & EVDEV_SEAT_TOUCH) + notify_touch(master, time, device->abs.seat_slot, + x, y, WL_TOUCH_MOTION); + else if (device->seat_caps & EVDEV_SEAT_POINTER) notify_motion_absolute(master, time, x, y); goto handled; case EVDEV_ABSOLUTE_TOUCH_UP: - notify_touch(master, time, 0, 0, 0, WL_TOUCH_UP); + seat_slot = device->abs.seat_slot; + master->slot_map &= ~(1 << seat_slot); + notify_touch(master, time, seat_slot, 0, 0, WL_TOUCH_UP); goto handled; } @@ -400,7 +413,7 @@ evdev_device_data(int fd, uint32_t mask, void *data) int len; ec = device->seat->compositor; - if (!ec->focus) + if (!ec->session_active) return 1; /* If the compositor is repainting, this function is called only once @@ -433,47 +446,40 @@ evdev_device_data(int fd, uint32_t mask, void *data) } static int -evdev_handle_device(struct evdev_device *device) +evdev_configure_device(struct evdev_device *device) { struct input_absinfo absinfo; unsigned long ev_bits[NBITS(EV_MAX)]; unsigned long abs_bits[NBITS(ABS_MAX)]; unsigned long rel_bits[NBITS(REL_MAX)]; unsigned long key_bits[NBITS(KEY_MAX)]; - int has_key, has_abs; + int has_abs, has_rel, has_mt; + int has_button, has_keyboard, has_touch; unsigned int i; - has_key = 0; + has_rel = 0; has_abs = 0; - device->caps = 0; + has_mt = 0; + has_button = 0; + has_keyboard = 0; + has_touch = 0; ioctl(device->fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits); if (TEST_BIT(ev_bits, EV_ABS)) { - has_abs = 1; - ioctl(device->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits); - if (TEST_BIT(abs_bits, ABS_WHEEL) || - TEST_BIT(abs_bits, ABS_GAS) || - TEST_BIT(abs_bits, ABS_BRAKE) || - TEST_BIT(abs_bits, ABS_HAT0X)) { - weston_log("device %s is a joystick, ignoring\n", - device->devnode); - return 0; - } - if (TEST_BIT(abs_bits, ABS_X)) { ioctl(device->fd, EVIOCGABS(ABS_X), &absinfo); device->abs.min_x = absinfo.minimum; device->abs.max_x = absinfo.maximum; - device->caps |= EVDEV_MOTION_ABS; + has_abs = 1; } if (TEST_BIT(abs_bits, ABS_Y)) { ioctl(device->fd, EVIOCGABS(ABS_Y), &absinfo); device->abs.min_y = absinfo.minimum; device->abs.max_y = absinfo.maximum; - device->caps |= EVDEV_MOTION_ABS; + has_abs = 1; } /* We only handle the slotted Protocol B in weston. Devices with ABS_MT_POSITION_* but not ABS_MT_SLOT @@ -489,7 +495,8 @@ evdev_handle_device(struct evdev_device *device) device->abs.min_y = absinfo.minimum; device->abs.max_y = absinfo.maximum; device->is_mt = 1; - device->caps |= EVDEV_TOUCH; + has_touch = 1; + has_mt = 1; if (!TEST_BIT(abs_bits, ABS_MT_SLOT)) { device->mtdev = mtdev_new_open(device->fd); @@ -510,15 +517,14 @@ evdev_handle_device(struct evdev_device *device) ioctl(device->fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)), rel_bits); if (TEST_BIT(rel_bits, REL_X) || TEST_BIT(rel_bits, REL_Y)) - device->caps |= EVDEV_MOTION_REL; + has_rel = 1; } if (TEST_BIT(ev_bits, EV_KEY)) { - has_key = 1; ioctl(device->fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits); if (TEST_BIT(key_bits, BTN_TOOL_FINGER) && !TEST_BIT(key_bits, BTN_TOOL_PEN) && - has_abs) { + (has_abs || has_mt)) { device->dispatch = evdev_touchpad_create(device); weston_log("input device %s, %s is a touchpad\n", device->devname, device->devnode); @@ -527,59 +533,39 @@ evdev_handle_device(struct evdev_device *device) if (i >= BTN_MISC && i < KEY_OK) continue; if (TEST_BIT(key_bits, i)) { - device->caps |= EVDEV_KEYBOARD; + has_keyboard = 1; break; } } - if (TEST_BIT(key_bits, BTN_TOUCH)) { - device->caps |= EVDEV_TOUCH; - } + if (TEST_BIT(key_bits, BTN_TOUCH)) + has_touch = 1; for (i = BTN_MISC; i < BTN_JOYSTICK; i++) { if (TEST_BIT(key_bits, i)) { - device->caps |= EVDEV_BUTTON; - device->caps &= ~EVDEV_TOUCH; + has_button = 1; break; } } } - if (TEST_BIT(ev_bits, EV_LED)) { - device->caps |= EVDEV_KEYBOARD; - } - - /* This rule tries to catch accelerometer devices and opt out. We may - * want to adjust the protocol later adding a proper event for dealing - * with accelerometers and implement here accordingly */ - if (has_abs && !has_key && !device->is_mt) { - weston_log("input device %s, %s " - "ignored: unsupported device type\n", - device->devname, device->devnode); - return 0; - } - - return 1; -} + if (TEST_BIT(ev_bits, EV_LED)) + has_keyboard = 1; -static int -evdev_configure_device(struct evdev_device *device) -{ - if ((device->caps & (EVDEV_MOTION_ABS | EVDEV_MOTION_REL)) && - (device->caps & EVDEV_BUTTON)) { + if ((has_abs || has_rel) && has_button) { weston_seat_init_pointer(device->seat); device->seat_caps |= EVDEV_SEAT_POINTER; weston_log("input device %s, %s is a pointer caps =%s%s%s\n", device->devname, device->devnode, - device->caps & EVDEV_MOTION_ABS ? " absolute-motion" : "", - device->caps & EVDEV_MOTION_REL ? " relative-motion": "", - device->caps & EVDEV_BUTTON ? " button" : ""); + has_abs ? " absolute-motion" : "", + has_rel ? " relative-motion": "", + has_button ? " button" : ""); } - if ((device->caps & EVDEV_KEYBOARD)) { + if (has_keyboard) { if (weston_seat_init_keyboard(device->seat, NULL) < 0) return -1; device->seat_caps |= EVDEV_SEAT_KEYBOARD; weston_log("input device %s, %s is a keyboard\n", device->devname, device->devnode); } - if ((device->caps & EVDEV_TOUCH)) { + if (has_touch && !has_button) { weston_seat_init_touch(device->seat); device->seat_caps |= EVDEV_SEAT_TOUCH; weston_log("input device %s, %s is a touch device\n", @@ -621,14 +607,14 @@ evdev_device_create(struct weston_seat *seat, const char *path, int device_fd) devname[sizeof(devname) - 1] = '\0'; device->devname = strdup(devname); - if (!evdev_handle_device(device)) { + if (evdev_configure_device(device) == -1) + goto err; + + if (device->seat_caps == 0) { evdev_device_destroy(device); return EVDEV_UNHANDLED_DEVICE; } - if (evdev_configure_device(device) == -1) - goto err; - /* If the dispatch was not set up use the fallback. */ if (device->dispatch == NULL) device->dispatch = fallback_dispatch_create(); @@ -672,6 +658,7 @@ evdev_device_destroy(struct evdev_device *device) close(device->fd); free(device->devname); free(device->devnode); + free(device->output_name); free(device); } @@ -687,7 +674,7 @@ evdev_notify_keyboard_focus(struct weston_seat *seat, uint32_t *k; int ret; - if (!seat->keyboard) + if (!seat->keyboard_device_count > 0) return; memset(all_keys, 0, sizeof all_keys); diff --git a/src/evdev.h b/src/evdev.h index e146d1a4..36a52e34 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -41,14 +41,6 @@ enum evdev_event_type { EVDEV_RELATIVE_MOTION, }; -enum evdev_device_capability { - EVDEV_KEYBOARD = (1 << 0), - EVDEV_BUTTON = (1 << 1), - EVDEV_MOTION_ABS = (1 << 2), - EVDEV_MOTION_REL = (1 << 3), - EVDEV_TOUCH = (1 << 4), -}; - enum evdev_device_seat_capability { EVDEV_SEAT_POINTER = (1 << 0), EVDEV_SEAT_KEYBOARD = (1 << 1), @@ -63,9 +55,11 @@ struct evdev_device { struct evdev_dispatch *dispatch; char *devnode; char *devname; + char *output_name; int fd; struct { int min_x, max_x, min_y, max_y; + uint32_t seat_slot; int32_t x, y; int apply_calibration; @@ -76,6 +70,7 @@ struct evdev_device { int slot; struct { int32_t x, y; + uint32_t seat_slot; } slots[MAX_SLOTS]; } mt; struct mtdev *mtdev; @@ -85,7 +80,6 @@ struct evdev_device { } rel; enum evdev_event_type pending_event; - enum evdev_device_capability caps; enum evdev_device_seat_capability seat_caps; int is_mt; diff --git a/src/filter.c b/src/filter.c index a55ebf27..89237bf8 100644 --- a/src/filter.c +++ b/src/filter.c @@ -323,7 +323,6 @@ create_pointer_accelator_filter(accel_profile_func_t profile) return NULL; filter->base.interface = &accelerator_interface; - wl_list_init(&filter->base.link); filter->profile = profile; filter->last_velocity = 0.0; diff --git a/src/filter.h b/src/filter.h index 850ce8df..dad538b2 100644 --- a/src/filter.h +++ b/src/filter.h @@ -50,7 +50,6 @@ struct weston_motion_filter_interface { struct weston_motion_filter { struct weston_motion_filter_interface *interface; - struct wl_list link; }; WL_EXPORT struct weston_motion_filter * diff --git a/src/gl-renderer.c b/src/gl-renderer.c index ae69f220..0e5afbe3 100644 --- a/src/gl-renderer.c +++ b/src/gl-renderer.c @@ -50,9 +50,18 @@ struct gl_shader { #define BUFFER_DAMAGE_COUNT 2 +struct gl_border_image { + GLuint tex; + int32_t width, height; + int32_t tex_width; + int dirty; + void *data; +}; + struct gl_output_state { EGLSurface egl_surface; pixman_region32_t buffer_damage[BUFFER_DAMAGE_COUNT]; + struct gl_border_image borders[4]; }; enum buffer_type { @@ -79,25 +88,25 @@ struct gl_surface_state { int pitch; /* in pixels */ int height; /* in pixels */ int y_inverted; + + struct weston_surface *surface; + + struct wl_listener surface_destroy_listener; + struct wl_listener renderer_destroy_listener; }; struct gl_renderer { struct weston_renderer base; int fragment_shader_debug; int fan_debug; + struct weston_binding *fragment_binding; + struct weston_binding *fan_binding; EGLDisplay egl_display; EGLContext egl_context; EGLConfig egl_config; - struct { - int32_t top, bottom, left, right; - GLuint texture; - int32_t width, height; - } border; - struct wl_array vertices; - struct wl_array indices; /* only used in compositor-wayland */ struct wl_array vtxcnt; PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d; @@ -124,6 +133,8 @@ struct gl_renderer { struct gl_shader invert_color_shader; struct gl_shader solid_shader; struct gl_shader *current_shader; + + struct wl_signal destroy_signal; }; static inline struct gl_output_state * @@ -132,9 +143,15 @@ get_output_state(struct weston_output *output) return (struct gl_output_state *)output->renderer_state; } +static int +gl_renderer_create_surface(struct weston_surface *surface); + static inline struct gl_surface_state * get_surface_state(struct weston_surface *surface) { + if (!surface->renderer_state) + gl_renderer_create_surface(surface); + return (struct gl_surface_state *)surface->renderer_state; } @@ -170,7 +187,7 @@ egl_error_string(EGLint code) #undef MYERRCODE } -WL_EXPORT void +static void gl_renderer_print_egl_error_state(void) { EGLint code; @@ -193,7 +210,7 @@ gl_renderer_print_egl_error_state(void) * polygon area. */ static int -calculate_edges(struct weston_surface *es, pixman_box32_t *rect, +calculate_edges(struct weston_view *ev, pixman_box32_t *rect, pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey) { @@ -213,8 +230,8 @@ calculate_edges(struct weston_surface *es, pixman_box32_t *rect, /* transform surface to screen space: */ for (i = 0; i < surf.n; i++) - weston_surface_to_global_float(es, surf.x[i], surf.y[i], - &surf.x[i], &surf.y[i]); + weston_view_to_global_float(ev, surf.x[i], surf.y[i], + &surf.x[i], &surf.y[i]); /* find bounding box: */ min_x = max_x = surf.x[0]; @@ -238,9 +255,8 @@ calculate_edges(struct weston_surface *es, pixman_box32_t *rect, * there will be only four edges. We just need to clip the surface * vertices to the clip rect bounds: */ - if (!es->transform.enabled) { + if (!ev->transform.enabled) return clip_simple(&ctx, &surf, ex, ey); - } /* Transformed case: use a general polygon clipping algorithm to * clip the surface rectangle with each side of 'rect'. @@ -257,11 +273,11 @@ calculate_edges(struct weston_surface *es, pixman_box32_t *rect, } static int -texture_region(struct weston_surface *es, pixman_region32_t *region, +texture_region(struct weston_view *ev, pixman_region32_t *region, pixman_region32_t *surf_region) { - struct gl_surface_state *gs = get_surface_state(es); - struct weston_compositor *ec = es->compositor; + struct gl_surface_state *gs = get_surface_state(ev->surface); + struct weston_compositor *ec = ev->surface->compositor; struct gl_renderer *gr = get_renderer(ec); GLfloat *v, inv_width, inv_height; unsigned int *vtxcnt, nvtx = 0; @@ -302,18 +318,20 @@ texture_region(struct weston_surface *es, pixman_region32_t *region, * form the intersection of the clip rect and the transformed * surface. */ - n = calculate_edges(es, rect, surf_rect, ex, ey); + n = calculate_edges(ev, rect, surf_rect, ex, ey); if (n < 3) continue; /* emit edge points: */ for (k = 0; k < n; k++) { - weston_surface_from_global_float(es, ex[k], ey[k], &sx, &sy); + weston_view_from_global_float(ev, ex[k], ey[k], + &sx, &sy); /* position: */ *(v++) = ex[k]; *(v++) = ey[k]; /* texcoord: */ - weston_surface_to_buffer_float(es, sx, sy, + weston_surface_to_buffer_float(ev->surface, + sx, sy, &bx, &by); *(v++) = bx * inv_width; if (gs->y_inverted) { @@ -331,9 +349,9 @@ texture_region(struct weston_surface *es, pixman_region32_t *region, } static void -triangle_fan_debug(struct weston_surface *surface, int first, int count) +triangle_fan_debug(struct weston_view *view, int first, int count) { - struct weston_compositor *compositor = surface->compositor; + struct weston_compositor *compositor = view->surface->compositor; struct gl_renderer *gr = get_renderer(compositor); int i; GLushort *buffer; @@ -371,10 +389,10 @@ triangle_fan_debug(struct weston_surface *surface, int first, int count) } static void -repaint_region(struct weston_surface *es, pixman_region32_t *region, +repaint_region(struct weston_view *ev, pixman_region32_t *region, pixman_region32_t *surf_region) { - struct weston_compositor *ec = es->compositor; + struct weston_compositor *ec = ev->surface->compositor; struct gl_renderer *gr = get_renderer(ec); GLfloat *v; unsigned int *vtxcnt; @@ -388,7 +406,7 @@ repaint_region(struct weston_surface *es, pixman_region32_t *region, * polygon for each pair, and store it as a triangle fan if * it has a non-zero area (at least 3 vertices1, actually). */ - nfans = texture_region(es, region, surf_region); + nfans = texture_region(ev, region, surf_region); v = gr->vertices.data; vtxcnt = gr->vtxcnt.data; @@ -404,7 +422,7 @@ repaint_region(struct weston_surface *es, pixman_region32_t *region, for (i = 0, first = 0; i < nfans; i++) { glDrawArrays(GL_TRIANGLE_FAN, first, vtxcnt[i]); if (gr->fan_debug) - triangle_fan_debug(es, first, vtxcnt[i]); + triangle_fan_debug(ev, first, vtxcnt[i]); first += vtxcnt[i]; } @@ -464,28 +482,28 @@ use_shader(struct gl_renderer *gr, struct gl_shader *shader) static void shader_uniforms(struct gl_shader *shader, - struct weston_surface *surface, - struct weston_output *output) + struct weston_view *view, + struct weston_output *output) { int i; - struct gl_surface_state *gs = get_surface_state(surface); + struct gl_surface_state *gs = get_surface_state(view->surface); glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, output->matrix.d); glUniform4fv(shader->color_uniform, 1, gs->color); - glUniform1f(shader->alpha_uniform, surface->alpha); + glUniform1f(shader->alpha_uniform, view->alpha); for (i = 0; i < gs->num_textures; i++) glUniform1i(shader->tex_uniforms[i], i); } static void -draw_surface(struct weston_surface *es, struct weston_output *output, - pixman_region32_t *damage) /* in global coordinates */ +draw_view(struct weston_view *ev, struct weston_output *output, + pixman_region32_t *damage) /* in global coordinates */ { - struct weston_compositor *ec = es->compositor; + struct weston_compositor *ec = ev->surface->compositor; struct gl_renderer *gr = get_renderer(ec); - struct gl_surface_state *gs = get_surface_state(es); + struct gl_surface_state *gs = get_surface_state(ev->surface); /* repaint bounding region in global coordinates: */ pixman_region32_t repaint; /* non-opaque region in surface coordinates: */ @@ -493,10 +511,16 @@ draw_surface(struct weston_surface *es, struct weston_output *output, GLint filter; int i; + /* In case of a runtime switch of renderers, we may not have received + * an attach for this surface since the switch. In that case we don't + * have a valid buffer or a proper shader set up so skip rendering. */ + if (!gs->shader) + return; + pixman_region32_init(&repaint); pixman_region32_intersect(&repaint, - &es->transform.boundingbox, damage); - pixman_region32_subtract(&repaint, &repaint, &es->clip); + &ev->transform.boundingbox, damage); + pixman_region32_subtract(&repaint, &repaint, &ev->clip); if (!pixman_region32_not_empty(&repaint)) goto out; @@ -505,13 +529,14 @@ draw_surface(struct weston_surface *es, struct weston_output *output, if (gr->fan_debug) { use_shader(gr, &gr->solid_shader); - shader_uniforms(&gr->solid_shader, es, output); + shader_uniforms(&gr->solid_shader, ev, output); } use_shader(gr, gs->shader); - shader_uniforms(gs->shader, es, output); + shader_uniforms(gs->shader, ev, output); - if (es->transform.enabled || output->zoom.active || output->current_scale != es->buffer_scale) + if (ev->transform.enabled || output->zoom.active || + output->current_scale != ev->surface->buffer_viewport.scale) filter = GL_LINEAR; else filter = GL_NEAREST; @@ -525,10 +550,11 @@ draw_surface(struct weston_surface *es, struct weston_output *output, /* blended region is whole surface minus opaque region: */ pixman_region32_init_rect(&surface_blend, 0, 0, - es->geometry.width, es->geometry.height); - pixman_region32_subtract(&surface_blend, &surface_blend, &es->opaque); + ev->surface->width, ev->surface->height); + pixman_region32_subtract(&surface_blend, &surface_blend, &ev->surface->opaque); - if (pixman_region32_not_empty(&es->opaque)) { + /* XXX: Should we be using ev->transform.opaque here? */ + if (pixman_region32_not_empty(&ev->surface->opaque)) { if (gs->shader == &gr->texture_shader_rgba) { /* Special case for RGBA textures with possibly * bad data in alpha channel: use the shader @@ -536,21 +562,21 @@ draw_surface(struct weston_surface *es, struct weston_output *output, * Xwayland surfaces need this. */ use_shader(gr, &gr->texture_shader_rgbx); - shader_uniforms(&gr->texture_shader_rgbx, es, output); + shader_uniforms(&gr->texture_shader_rgbx, ev, output); } - if (es->alpha < 1.0) + if (ev->alpha < 1.0) glEnable(GL_BLEND); else glDisable(GL_BLEND); - repaint_region(es, &repaint, &es->opaque); + repaint_region(ev, &repaint, &ev->surface->opaque); } if (pixman_region32_not_empty(&surface_blend)) { use_shader(gr, gs->shader); glEnable(GL_BLEND); - repaint_region(es, &repaint, &surface_blend); + repaint_region(ev, &repaint, &surface_blend); } pixman_region32_fini(&surface_blend); @@ -560,130 +586,128 @@ out: } static void -repaint_surfaces(struct weston_output *output, pixman_region32_t *damage) +repaint_views(struct weston_output *output, pixman_region32_t *damage) { struct weston_compositor *compositor = output->compositor; - struct weston_surface *surface; + struct weston_view *view; - wl_list_for_each_reverse(surface, &compositor->surface_list, link) - if (surface->plane == &compositor->primary_plane) - draw_surface(surface, output, damage); + wl_list_for_each_reverse(view, &compositor->view_list, link) + if (view->plane == &compositor->primary_plane) + draw_view(view, output, damage); } - -static int -texture_border(struct weston_output *output) +static void +draw_output_border_texture(struct gl_border_image *img, int32_t x, int32_t y, + int32_t width, int32_t height) { - struct weston_compositor *ec = output->compositor; - struct gl_renderer *gr = get_renderer(ec); - GLfloat *d; - unsigned short *p; - int i, j, k, n; - GLfloat x[4], y[4], u[4], v[4]; - - x[0] = -gr->border.left; - x[1] = 0; - x[2] = output->current_mode->width; - x[3] = output->current_mode->width + gr->border.right; - - y[0] = -gr->border.top; - y[1] = 0; - y[2] = output->current_mode->height; - y[3] = output->current_mode->height + gr->border.bottom; - - u[0] = 0.0; - u[1] = (GLfloat) gr->border.left / gr->border.width; - u[2] = (GLfloat) (gr->border.width - gr->border.right) / gr->border.width; - u[3] = 1.0; - - v[0] = 0.0; - v[1] = (GLfloat) gr->border.top / gr->border.height; - v[2] = (GLfloat) (gr->border.height - gr->border.bottom) / gr->border.height; - v[3] = 1.0; - - n = 8; - d = wl_array_add(&gr->vertices, n * 16 * sizeof *d); - p = wl_array_add(&gr->indices, n * 6 * sizeof *p); - - k = 0; - for (i = 0; i < 3; i++) - for (j = 0; j < 3; j++) { - - if (i == 1 && j == 1) - continue; + static GLushort indices [] = { 0, 1, 3, 3, 1, 2 }; - d[ 0] = x[i]; - d[ 1] = y[j]; - d[ 2] = u[i]; - d[ 3] = v[j]; - - d[ 4] = x[i]; - d[ 5] = y[j + 1]; - d[ 6] = u[i]; - d[ 7] = v[j + 1]; - - d[ 8] = x[i + 1]; - d[ 9] = y[j]; - d[10] = u[i + 1]; - d[11] = v[j]; - - d[12] = x[i + 1]; - d[13] = y[j + 1]; - d[14] = u[i + 1]; - d[15] = v[j + 1]; - - p[0] = k + 0; - p[1] = k + 1; - p[2] = k + 2; - p[3] = k + 2; - p[4] = k + 1; - p[5] = k + 3; - - d += 16; - p += 6; - k += 4; + if (!img->data) { + if (img->tex) { + glDeleteTextures(1, &img->tex); + img->tex = 0; } - return k / 4; + return; + } + + if (!img->tex) { + glGenTextures(1, &img->tex); + glBindTexture(GL_TEXTURE_2D, img->tex); + + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } else { + glBindTexture(GL_TEXTURE_2D, img->tex); + } + + if (img->dirty) { +#ifdef GL_EXT_unpack_subimage + glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); +#endif + glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, + img->tex_width, img->height, 0, + GL_BGRA_EXT, GL_UNSIGNED_BYTE, img->data); + img->dirty = 0; + } + + GLfloat texcoord[] = { + 0.0f, 0.0f, + (GLfloat)img->width / (GLfloat)img->tex_width, 0.0f, + (GLfloat)img->width / (GLfloat)img->tex_width, 1.0f, + 0.0f, 1.0f, + }; + + GLfloat verts[] = { + x, y, + x + width, y, + x + width, y + height, + x, y + height + }; + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texcoord); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices); + + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(0); } static void -draw_border(struct weston_output *output) +draw_output_border(struct weston_output *output) { - struct weston_compositor *ec = output->compositor; - struct gl_renderer *gr = get_renderer(ec); + struct gl_output_state *go = get_output_state(output); + struct gl_renderer *gr = get_renderer(output->compositor); struct gl_shader *shader = &gr->texture_shader_rgba; - GLfloat *v; - int n; + struct gl_border_image *top, *bottom, *left, *right; + struct weston_matrix matrix; + int full_width, full_height; + + top = &go->borders[GL_RENDERER_BORDER_TOP]; + bottom = &go->borders[GL_RENDERER_BORDER_BOTTOM]; + left = &go->borders[GL_RENDERER_BORDER_LEFT]; + right = &go->borders[GL_RENDERER_BORDER_RIGHT]; + + full_width = output->current_mode->width + left->width + right->width; + full_height = output->current_mode->height + top->height + bottom->height; glDisable(GL_BLEND); use_shader(gr, shader); - glUniformMatrix4fv(shader->proj_uniform, - 1, GL_FALSE, output->matrix.d); + glViewport(0, 0, full_width, full_height); + + weston_matrix_init(&matrix); + weston_matrix_translate(&matrix, -full_width/2.0, -full_height/2.0, 0); + weston_matrix_scale(&matrix, 2.0/full_width, -2.0/full_height, 1); + glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, matrix.d); glUniform1i(shader->tex_uniforms[0], 0); glUniform1f(shader->alpha_uniform, 1); - - n = texture_border(output); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, gr->border.texture); - v = gr->vertices.data; - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[0]); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof *v, &v[2]); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - - glDrawElements(GL_TRIANGLES, n * 6, - GL_UNSIGNED_SHORT, gr->indices.data); - - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(0); - - gr->vertices.size = 0; - gr->indices.size = 0; + draw_output_border_texture(top, + 0, 0, + full_width, top->height); + draw_output_border_texture(left, + 0, top->height, + left->width, output->current_mode->height); + draw_output_border_texture(right, + full_width - right->width, top->height, + right->width, output->current_mode->height); + draw_output_border_texture(bottom, + 0, full_height - bottom->height, + full_width, bottom->height); } static void @@ -740,15 +764,13 @@ gl_renderer_repaint_output(struct weston_output *output, struct gl_renderer *gr = get_renderer(compositor); EGLBoolean ret; static int errored; - int32_t width, height; pixman_region32_t buffer_damage, total_damage; - width = output->current_mode->width + - output->border.left + output->border.right; - height = output->current_mode->height + - output->border.top + output->border.bottom; - - glViewport(0, 0, width, height); + /* Calculate the viewport */ + glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width, + go->borders[GL_RENDERER_BORDER_BOTTOM].height, + output->current_mode->width, + output->current_mode->height); if (use_output(output) < 0) return; @@ -762,7 +784,7 @@ gl_renderer_repaint_output(struct weston_output *output, pixman_region32_subtract(&undamaged, &output->region, output_damage); gr->fan_debug = 0; - repaint_surfaces(output, &undamaged); + repaint_views(output, &undamaged); gr->fan_debug = 1; pixman_region32_fini(&undamaged); } @@ -775,13 +797,12 @@ gl_renderer_repaint_output(struct weston_output *output, pixman_region32_union(&total_damage, &buffer_damage, output_damage); - repaint_surfaces(output, &total_damage); + repaint_views(output, &total_damage); pixman_region32_fini(&total_damage); pixman_region32_fini(&buffer_damage); - if (gr->border.texture) - draw_border(output); + draw_output_border(output); pixman_region32_copy(&output->previous_damage, output_damage); wl_signal_emit(&output->frame_signal, output); @@ -830,6 +851,8 @@ gl_renderer_flush_damage(struct weston_surface *surface) struct gl_renderer *gr = get_renderer(surface->compositor); struct gl_surface_state *gs = get_surface_state(surface); struct weston_buffer *buffer = gs->buffer_ref.buffer; + struct weston_view *view; + int texture_used; GLenum format; int pixel_type; @@ -850,10 +873,18 @@ gl_renderer_flush_damage(struct weston_surface *surface) * hold the reference to the buffer, in case the surface * migrates back to the primary plane. */ - if (surface->plane != &surface->compositor->primary_plane) + texture_used = 0; + wl_list_for_each(view, &surface->views, surface_link) { + if (view->plane == &surface->compositor->primary_plane) { + texture_used = 1; + break; + } + } + if (!texture_used) return; - if (!pixman_region32_not_empty(&gs->texture_damage)) + if (!pixman_region32_not_empty(&gs->texture_damage) && + !gs->needs_full_upload) goto done; switch (wl_shm_buffer_get_format(buffer->shm_buffer)) { @@ -875,10 +906,12 @@ gl_renderer_flush_damage(struct weston_surface *surface) glBindTexture(GL_TEXTURE_2D, gs->textures[0]); if (!gr->has_unpack_subimage) { + wl_shm_buffer_begin_access(buffer->shm_buffer); glTexImage2D(GL_TEXTURE_2D, 0, format, gs->pitch, buffer->height, 0, format, pixel_type, wl_shm_buffer_get_data(buffer->shm_buffer)); + wl_shm_buffer_end_access(buffer->shm_buffer); goto done; } @@ -890,13 +923,16 @@ gl_renderer_flush_damage(struct weston_surface *surface) if (gs->needs_full_upload) { glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); + wl_shm_buffer_begin_access(buffer->shm_buffer); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, gs->pitch, buffer->height, format, pixel_type, data); + wl_shm_buffer_end_access(buffer->shm_buffer); goto done; } rectangles = pixman_region32_rectangles(&gs->texture_damage, &n); + wl_shm_buffer_begin_access(buffer->shm_buffer); for (i = 0; i < n; i++) { pixman_box32_t r; @@ -908,6 +944,7 @@ gl_renderer_flush_damage(struct weston_surface *surface) r.x2 - r.x1, r.y2 - r.y1, format, pixel_type, data); } + wl_shm_buffer_end_access(buffer->shm_buffer); #endif done: @@ -983,6 +1020,8 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, gs->needs_full_upload = 1; gs->y_inverted = 1; + gs->surface = es; + ensure_textures(gs, 1); glBindTexture(GL_TEXTURE_2D, gs->textures[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, @@ -1122,10 +1161,59 @@ gl_renderer_surface_set_color(struct weston_surface *surface, gs->shader = &gr->solid_shader; } +static void +surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr) +{ + int i; + + wl_list_remove(&gs->surface_destroy_listener.link); + wl_list_remove(&gs->renderer_destroy_listener.link); + + gs->surface->renderer_state = NULL; + + glDeleteTextures(gs->num_textures, gs->textures); + + for (i = 0; i < gs->num_images; i++) + gr->destroy_image(gr->egl_display, gs->images[i]); + + weston_buffer_reference(&gs->buffer_ref, NULL); + pixman_region32_fini(&gs->texture_damage); + free(gs); +} + +static void +surface_state_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct gl_surface_state *gs; + struct gl_renderer *gr; + + gs = container_of(listener, struct gl_surface_state, + surface_destroy_listener); + + gr = get_renderer(gs->surface->compositor); + + surface_state_destroy(gs, gr); +} + +static void +surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data) +{ + struct gl_surface_state *gs; + struct gl_renderer *gr; + + gr = data; + + gs = container_of(listener, struct gl_surface_state, + renderer_destroy_listener); + + surface_state_destroy(gs, gr); +} + static int gl_renderer_create_surface(struct weston_surface *surface) { struct gl_surface_state *gs; + struct gl_renderer *gr = get_renderer(surface->compositor); gs = calloc(1, sizeof *gs); if (!gs) @@ -1138,27 +1226,27 @@ gl_renderer_create_surface(struct weston_surface *surface) gs->pitch = 1; gs->y_inverted = 1; + gs->surface = surface; + pixman_region32_init(&gs->texture_damage); surface->renderer_state = gs; - return 0; -} - -static void -gl_renderer_destroy_surface(struct weston_surface *surface) -{ - struct gl_surface_state *gs = get_surface_state(surface); - struct gl_renderer *gr = get_renderer(surface->compositor); - int i; + gs->surface_destroy_listener.notify = + surface_state_handle_surface_destroy; + wl_signal_add(&surface->destroy_signal, + &gs->surface_destroy_listener); - glDeleteTextures(gs->num_textures, gs->textures); + gs->renderer_destroy_listener.notify = + surface_state_handle_renderer_destroy; + wl_signal_add(&gr->destroy_signal, + &gs->renderer_destroy_listener); - for (i = 0; i < gs->num_images; i++) - gr->destroy_image(gr->egl_display, gs->images[i]); + if (surface->buffer_ref.buffer) { + gl_renderer_attach(surface, surface->buffer_ref.buffer); + gl_renderer_flush_damage(surface); + } - weston_buffer_reference(&gs->buffer_ref, NULL); - pixman_region32_fini(&gs->texture_damage); - free(gs); + return 0; } static const char vertex_shader[] = @@ -1432,50 +1520,24 @@ log_egl_config_info(EGLDisplay egldpy, EGLConfig eglconfig) } static void -output_apply_border(struct weston_output *output, struct gl_renderer *gr) +gl_renderer_output_set_border(struct weston_output *output, + enum gl_renderer_border_side side, + int32_t width, int32_t height, + int32_t tex_width, unsigned char *data) { - output->border.top = gr->border.top; - output->border.bottom = gr->border.bottom; - output->border.left = gr->border.left; - output->border.right = gr->border.right; -} - -WL_EXPORT void -gl_renderer_set_border(struct weston_compositor *ec, int32_t width, int32_t height, void *data, - int32_t *edges) -{ - struct gl_renderer *gr = get_renderer(ec); - struct weston_output *output; - - gr->border.left = edges[0]; - gr->border.right = edges[1]; - gr->border.top = edges[2]; - gr->border.bottom = edges[3]; - - gr->border.width = width; - gr->border.height = height; - - glGenTextures(1, &gr->border.texture); - glBindTexture(GL_TEXTURE_2D, gr->border.texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, - width, - height, - 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, - data); + struct gl_output_state *go = get_output_state(output); - wl_list_for_each(output, &ec->output_list, link) - output_apply_border(output, gr); + go->borders[side].width = width; + go->borders[side].height = height; + go->borders[side].tex_width = tex_width; + go->borders[side].data = data; + go->borders[side].dirty = 1; } static int gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface); -WL_EXPORT int +static int gl_renderer_output_create(struct weston_output *output, EGLNativeWindowType window) { @@ -1509,12 +1571,10 @@ gl_renderer_output_create(struct weston_output *output, output->renderer_state = go; - output_apply_border(output, gr); - return 0; } -WL_EXPORT void +static void gl_renderer_output_destroy(struct weston_output *output) { struct gl_renderer *gr = get_renderer(output->compositor); @@ -1529,7 +1589,7 @@ gl_renderer_output_destroy(struct weston_output *output) free(go); } -WL_EXPORT EGLSurface +static EGLSurface gl_renderer_output_surface(struct weston_output *output) { return get_output_state(output)->egl_surface; @@ -1540,6 +1600,8 @@ gl_renderer_destroy(struct weston_compositor *ec) { struct gl_renderer *gr = get_renderer(ec); + wl_signal_emit(&gr->destroy_signal, gr); + if (gr->has_bind_display) gr->unbind_display(gr->egl_display, ec->wl_display); @@ -1552,9 +1614,11 @@ gl_renderer_destroy(struct weston_compositor *ec) eglReleaseThread(); wl_array_release(&gr->vertices); - wl_array_release(&gr->indices); wl_array_release(&gr->vtxcnt); + weston_binding_destroy(gr->fragment_binding); + weston_binding_destroy(gr->fan_binding); + free(gr); } @@ -1602,7 +1666,7 @@ out: return -1; } -WL_EXPORT const EGLint gl_renderer_opaque_attribs[] = { +static const EGLint gl_renderer_opaque_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, @@ -1612,7 +1676,7 @@ WL_EXPORT const EGLint gl_renderer_opaque_attribs[] = { EGL_NONE }; -WL_EXPORT const EGLint gl_renderer_alpha_attribs[] = { +static const EGLint gl_renderer_alpha_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, @@ -1622,7 +1686,7 @@ WL_EXPORT const EGLint gl_renderer_alpha_attribs[] = { EGL_NONE }; -WL_EXPORT int +static int gl_renderer_create(struct weston_compositor *ec, EGLNativeDisplayType display, const EGLint *attribs, const EGLint *visual_id) { @@ -1638,9 +1702,7 @@ gl_renderer_create(struct weston_compositor *ec, EGLNativeDisplayType display, gr->base.repaint_output = gl_renderer_repaint_output; gr->base.flush_damage = gl_renderer_flush_damage; gr->base.attach = gl_renderer_attach; - gr->base.create_surface = gl_renderer_create_surface; gr->base.surface_set_color = gl_renderer_surface_set_color; - gr->base.destroy_surface = gl_renderer_destroy_surface; gr->base.destroy = gl_renderer_destroy; gr->egl_display = eglGetDisplay(display); @@ -1665,6 +1727,8 @@ gl_renderer_create(struct weston_compositor *ec, EGLNativeDisplayType display, wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565); + wl_signal_init(&gr->destroy_signal); + return 0; err_egl: @@ -1673,7 +1737,7 @@ err_egl: return -1; } -WL_EXPORT EGLDisplay +static EGLDisplay gl_renderer_display(struct weston_compositor *ec) { return get_renderer(ec)->egl_display; @@ -1701,7 +1765,7 @@ compile_shaders(struct weston_compositor *ec) gr->texture_shader_y_u_v.fragment_source = texture_fragment_shader_y_u_v; - gr->texture_shader_y_u_v.vertex_source = vertex_shader; + gr->texture_shader_y_xuxv.vertex_source = vertex_shader; gr->texture_shader_y_xuxv.fragment_source = texture_fragment_shader_y_xuxv; @@ -1847,10 +1911,14 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) if (compile_shaders(ec)) return -1; - weston_compositor_add_debug_binding(ec, KEY_S, - fragment_debug_binding, ec); - weston_compositor_add_debug_binding(ec, KEY_F, - fan_debug_repaint_binding, ec); + gr->fragment_binding = + weston_compositor_add_debug_binding(ec, KEY_S, + fragment_debug_binding, + ec); + gr->fan_binding = + weston_compositor_add_debug_binding(ec, KEY_F, + fan_debug_repaint_binding, + ec); weston_log("GL ES 2 renderer features:\n"); weston_log_continue(STAMP_SPACE "read-back format: %s\n", @@ -1863,3 +1931,16 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) return 0; } + +WL_EXPORT struct gl_renderer_interface gl_renderer_interface = { + .opaque_attribs = gl_renderer_opaque_attribs, + .alpha_attribs = gl_renderer_alpha_attribs, + + .create = gl_renderer_create, + .display = gl_renderer_display, + .output_create = gl_renderer_output_create, + .output_destroy = gl_renderer_output_destroy, + .output_surface = gl_renderer_output_surface, + .output_set_border = gl_renderer_output_set_border, + .print_egl_error_state = gl_renderer_print_egl_error_state +}; diff --git a/src/gl-renderer.h b/src/gl-renderer.h index d16ade29..dcdf69d8 100644 --- a/src/gl-renderer.h +++ b/src/gl-renderer.h @@ -28,27 +28,6 @@ #include -extern const EGLint gl_renderer_opaque_attribs[]; -extern const EGLint gl_renderer_alpha_attribs[]; - -int -gl_renderer_create(struct weston_compositor *ec, EGLNativeDisplayType display, - const EGLint *attribs, const EGLint *visual_id); -EGLDisplay -gl_renderer_display(struct weston_compositor *ec); -int -gl_renderer_output_create(struct weston_output *output, - EGLNativeWindowType window); -void -gl_renderer_output_destroy(struct weston_output *output); -EGLSurface -gl_renderer_output_surface(struct weston_output *output); -void -gl_renderer_set_border(struct weston_compositor *ec, int32_t width, int32_t height, void *data, - int32_t *edges); - -void -gl_renderer_print_egl_error_state(void); #else typedef int EGLint; @@ -56,51 +35,68 @@ typedef void *EGLDisplay; typedef void *EGLSurface; typedef intptr_t EGLNativeDisplayType; typedef intptr_t EGLNativeWindowType; -#define EGL_DEFAULT_DISPLAY NULL - -static const EGLint gl_renderer_opaque_attribs[]; -static const EGLint gl_renderer_alpha_attribs[]; - -inline static int -gl_renderer_create(struct weston_compositor *ec, EGLNativeDisplayType display, - const EGLint *attribs, const EGLint *visual_id) -{ - return -1; -} - -inline static EGLDisplay -gl_renderer_display(struct weston_compositor *ec) -{ - return 0; -} - -inline static int -gl_renderer_output_create(struct weston_output *output, - EGLNativeWindowType window) -{ - return -1; -} - -inline static void -gl_renderer_output_destroy(struct weston_output *output) -{ -} - -inline static EGLSurface -gl_renderer_output_surface(struct weston_output *output) -{ - return 0; -} - -inline static void -gl_renderer_set_border(struct weston_compositor *ec, int32_t width, int32_t height, void *data, - int32_t *edges) -{ -} - -inline static void -gl_renderer_print_egl_error_state(void) -{ -} +#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0) #endif + +enum gl_renderer_border_side { + GL_RENDERER_BORDER_TOP = 0, + GL_RENDERER_BORDER_LEFT = 1, + GL_RENDERER_BORDER_RIGHT = 2, + GL_RENDERER_BORDER_BOTTOM = 3, +}; + +struct gl_renderer_interface { + const EGLint *opaque_attribs; + const EGLint *alpha_attribs; + + int (*create)(struct weston_compositor *ec, + EGLNativeDisplayType display, + const EGLint *attribs, + const EGLint *visual_id); + + EGLDisplay (*display)(struct weston_compositor *ec); + + int (*output_create)(struct weston_output *output, + EGLNativeWindowType window); + + void (*output_destroy)(struct weston_output *output); + + EGLSurface (*output_surface)(struct weston_output *output); + + /* Sets the output border. + * + * The side specifies the side for which we are setting the border. + * The width and height are the width and height of the border. + * The tex_width patemeter specifies the width of the actual + * texture; this may be larger than width if the data is not + * tightly packed. + * + * The top and bottom textures will extend over the sides to the + * full width of the bordered window while. The right and left + * edges, however, will extend only to the top and bottom of the + * compositor surface. This is demonstrated by the picture below: + * + * +-----------------------+ + * | TOP | + * +-+-------------------+-+ + * | | | | + * |L| |R| + * |E| |I| + * |F| |G| + * |T| |H| + * | | |T| + * | | | | + * +-+-------------------+-+ + * | BOTTOM | + * +-----------------------+ + */ + void (*output_set_border)(struct weston_output *output, + enum gl_renderer_border_side side, + int32_t width, int32_t height, + int32_t tex_width, unsigned char *data); + + void (*print_egl_error_state)(void); +}; + +struct gl_renderer_interface gl_renderer_interface; diff --git a/src/input.c b/src/input.c index dd8e4973..157066cc 100644 --- a/src/input.c +++ b/src/input.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "../shared/os-compatibility.h" #include "compositor.h" @@ -70,6 +71,56 @@ weston_compositor_idle_release(struct weston_compositor *compositor) weston_compositor_wake(compositor); } +static void +pointer_focus_view_destroyed(struct wl_listener *listener, void *data) +{ + struct weston_pointer *pointer = + container_of(listener, struct weston_pointer, + focus_view_listener); + + weston_pointer_set_focus(pointer, NULL, 0, 0); +} + +static void +pointer_focus_resource_destroyed(struct wl_listener *listener, void *data) +{ + struct weston_pointer *pointer = + container_of(listener, struct weston_pointer, + focus_resource_listener); + + weston_pointer_set_focus(pointer, NULL, 0, 0); +} + +static void +keyboard_focus_resource_destroyed(struct wl_listener *listener, void *data) +{ + struct weston_keyboard *keyboard = + container_of(listener, struct weston_keyboard, + focus_resource_listener); + + weston_keyboard_set_focus(keyboard, NULL); +} + +static void +touch_focus_view_destroyed(struct wl_listener *listener, void *data) +{ + struct weston_touch *touch = + container_of(listener, struct weston_touch, + focus_view_listener); + + weston_touch_set_focus(touch->seat, NULL); +} + +static void +touch_focus_resource_destroyed(struct wl_listener *listener, void *data) +{ + struct weston_touch *touch = + container_of(listener, struct weston_touch, + focus_resource_listener); + + weston_touch_set_focus(touch->seat, NULL); +} + static void move_resources(struct wl_list *destination, struct wl_list *source) { @@ -93,47 +144,50 @@ move_resources_for_client(struct wl_list *destination, } static void -default_grab_focus(struct weston_pointer_grab *grab) +default_grab_pointer_focus(struct weston_pointer_grab *grab) { struct weston_pointer *pointer = grab->pointer; - struct weston_surface *surface; + struct weston_view *view; wl_fixed_t sx, sy; if (pointer->button_count > 0) return; - surface = weston_compositor_pick_surface(pointer->seat->compositor, - pointer->x, pointer->y, - &sx, &sy); + view = weston_compositor_pick_view(pointer->seat->compositor, + pointer->x, pointer->y, + &sx, &sy); - if (pointer->focus != surface) - weston_pointer_set_focus(pointer, surface, sx, sy); + if (pointer->focus != view) + weston_pointer_set_focus(pointer, view, sx, sy); } static void -default_grab_motion(struct weston_pointer_grab *grab, uint32_t time) +default_grab_pointer_motion(struct weston_pointer_grab *grab, uint32_t time, + wl_fixed_t x, wl_fixed_t y) { struct weston_pointer *pointer = grab->pointer; wl_fixed_t sx, sy; struct wl_list *resource_list; struct wl_resource *resource; + weston_pointer_move(pointer, x, y); + resource_list = &pointer->focus_resource_list; wl_resource_for_each(resource, resource_list) { - weston_surface_from_global_fixed(pointer->focus, - pointer->x, pointer->y, - &sx, &sy); + weston_view_from_global_fixed(pointer->focus, + pointer->x, pointer->y, + &sx, &sy); wl_pointer_send_motion(resource, time, sx, sy); } } static void -default_grab_button(struct weston_pointer_grab *grab, - uint32_t time, uint32_t button, uint32_t state_w) +default_grab_pointer_button(struct weston_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state_w) { struct weston_pointer *pointer = grab->pointer; struct weston_compositor *compositor = pointer->seat->compositor; - struct weston_surface *surface; + struct weston_view *view; struct wl_resource *resource; uint32_t serial; enum wl_pointer_button_state state = state_w; @@ -154,12 +208,11 @@ default_grab_button(struct weston_pointer_grab *grab, if (pointer->button_count == 0 && state == WL_POINTER_BUTTON_STATE_RELEASED) { - surface = weston_compositor_pick_surface(compositor, - pointer->x, - pointer->y, - &sx, &sy); + view = weston_compositor_pick_view(compositor, + pointer->x, pointer->y, + &sx, &sy); - weston_pointer_set_focus(pointer, surface, sx, sy); + weston_pointer_set_focus(pointer, view, sx, sy); } } @@ -170,9 +223,9 @@ default_grab_pointer_cancel(struct weston_pointer_grab *grab) static const struct weston_pointer_grab_interface default_pointer_grab_interface = { - default_grab_focus, - default_grab_motion, - default_grab_button, + default_grab_pointer_focus, + default_grab_pointer_motion, + default_grab_pointer_button, default_grab_pointer_cancel, }; @@ -192,7 +245,7 @@ default_grab_touch_down(struct weston_touch_grab *grab, uint32_t time, serial = wl_display_next_serial(display); wl_resource_for_each(resource, resource_list) wl_touch_send_down(resource, serial, time, - touch->focus->resource, + touch->focus->surface->resource, touch_id, sx, sy); } } @@ -245,8 +298,8 @@ static const struct weston_touch_grab_interface default_touch_grab_interface = { }; static void -default_grab_key(struct weston_keyboard_grab *grab, - uint32_t time, uint32_t key, uint32_t state) +default_grab_keyboard_key(struct weston_keyboard_grab *grab, + uint32_t time, uint32_t key, uint32_t state) { struct weston_keyboard *keyboard = grab->keyboard; struct wl_resource *resource; @@ -307,10 +360,20 @@ find_resource_for_surface(struct wl_list *list, struct weston_surface *surface) return wl_resource_find_for_client(list, wl_resource_get_client(surface->resource)); } +static struct wl_resource * +find_resource_for_view(struct wl_list *list, struct weston_view *view) +{ + if (!view) + return NULL; + + return find_resource_for_surface(list, view->surface); +} + static void -default_grab_modifiers(struct weston_keyboard_grab *grab, uint32_t serial, - uint32_t mods_depressed, uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) +default_grab_keyboard_modifiers(struct weston_keyboard_grab *grab, + uint32_t serial, uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { struct weston_keyboard *keyboard = grab->keyboard; struct weston_pointer *pointer = grab->keyboard->seat->pointer; @@ -323,9 +386,10 @@ default_grab_modifiers(struct weston_keyboard_grab *grab, uint32_t serial, wl_keyboard_send_modifiers(resource, serial, mods_depressed, mods_latched, mods_locked, group); } - if (pointer && pointer->focus && pointer->focus != keyboard->focus) { + if (pointer && pointer->focus && pointer->focus->surface->resource && + pointer->focus->surface != keyboard->focus) { struct wl_client *pointer_client = - wl_resource_get_client(pointer->focus->resource); + wl_resource_get_client(pointer->focus->surface->resource); send_modifiers_to_client_in_list(pointer_client, &keyboard->resource_list, serial, @@ -340,20 +404,21 @@ default_grab_keyboard_cancel(struct weston_keyboard_grab *grab) static const struct weston_keyboard_grab_interface default_keyboard_grab_interface = { - default_grab_key, - default_grab_modifiers, + default_grab_keyboard_key, + default_grab_keyboard_modifiers, default_grab_keyboard_cancel, }; static void pointer_unmap_sprite(struct weston_pointer *pointer) { - if (weston_surface_is_mapped(pointer->sprite)) - weston_surface_unmap(pointer->sprite); + if (weston_surface_is_mapped(pointer->sprite->surface)) + weston_surface_unmap(pointer->sprite->surface); wl_list_remove(&pointer->sprite_destroy_listener.link); - pointer->sprite->configure = NULL; - pointer->sprite->configure_private = NULL; + pointer->sprite->surface->configure = NULL; + pointer->sprite->surface->configure_private = NULL; + weston_view_destroy(pointer->sprite); pointer->sprite = NULL; } @@ -367,8 +432,14 @@ pointer_handle_sprite_destroy(struct wl_listener *listener, void *data) pointer->sprite = NULL; } +static void +weston_pointer_reset_state(struct weston_pointer *pointer) +{ + pointer->button_count = 0; +} + WL_EXPORT struct weston_pointer * -weston_pointer_create(void) +weston_pointer_create(struct weston_seat *seat) { struct weston_pointer *pointer; @@ -378,10 +449,15 @@ weston_pointer_create(void) wl_list_init(&pointer->resource_list); wl_list_init(&pointer->focus_resource_list); - pointer->default_grab.interface = &default_pointer_grab_interface; + weston_pointer_set_default_grab(pointer, + seat->compositor->default_pointer_grab); + wl_list_init(&pointer->focus_resource_listener.link); + pointer->focus_resource_listener.notify = pointer_focus_resource_destroyed; pointer->default_grab.pointer = pointer; pointer->grab = &pointer->default_grab; + wl_signal_init(&pointer->motion_signal); wl_signal_init(&pointer->focus_signal); + wl_list_init(&pointer->focus_view_listener.link); pointer->sprite_destroy_listener.notify = pointer_handle_sprite_destroy; @@ -400,9 +476,22 @@ weston_pointer_destroy(struct weston_pointer *pointer) /* XXX: What about pointer->resource_list? */ + wl_list_remove(&pointer->focus_resource_listener.link); + wl_list_remove(&pointer->focus_view_listener.link); free(pointer); } +void +weston_pointer_set_default_grab(struct weston_pointer *pointer, + const struct weston_pointer_grab_interface *interface) +{ + if (interface) + pointer->default_grab.interface = interface; + else + pointer->default_grab.interface = + &default_pointer_grab_interface; +} + WL_EXPORT struct weston_keyboard * weston_keyboard_create(void) { @@ -414,6 +503,8 @@ weston_keyboard_create(void) wl_list_init(&keyboard->resource_list); wl_list_init(&keyboard->focus_resource_list); + wl_list_init(&keyboard->focus_resource_listener.link); + keyboard->focus_resource_listener.notify = keyboard_focus_resource_destroyed; wl_array_init(&keyboard->keys); keyboard->default_grab.interface = &default_keyboard_grab_interface; keyboard->default_grab.keyboard = keyboard; @@ -423,15 +514,36 @@ weston_keyboard_create(void) return keyboard; } +static void +weston_xkb_info_destroy(struct weston_xkb_info *xkb_info); + WL_EXPORT void weston_keyboard_destroy(struct weston_keyboard *keyboard) { /* XXX: What about keyboard->resource_list? */ +#ifdef ENABLE_XKBCOMMON + if (keyboard->seat->compositor->use_xkbcommon) { + if (keyboard->xkb_state.state != NULL) + xkb_state_unref(keyboard->xkb_state.state); + if (keyboard->xkb_info) + weston_xkb_info_destroy(keyboard->xkb_info); + if (keyboard->pending_keymap) + xkb_keymap_unref(keyboard->pending_keymap); + } +#endif + wl_array_release(&keyboard->keys); + wl_list_remove(&keyboard->focus_resource_listener.link); free(keyboard); } +static void +weston_touch_reset_state(struct weston_touch *touch) +{ + touch->num_tp = 0; +} + WL_EXPORT struct weston_touch * weston_touch_create(void) { @@ -443,6 +555,10 @@ weston_touch_create(void) wl_list_init(&touch->resource_list); wl_list_init(&touch->focus_resource_list); + wl_list_init(&touch->focus_view_listener.link); + touch->focus_view_listener.notify = touch_focus_view_destroyed; + wl_list_init(&touch->focus_resource_listener.link); + touch->focus_resource_listener.notify = touch_focus_resource_destroyed; touch->default_grab.interface = &default_touch_grab_interface; touch->default_grab.touch = touch; touch->grab = &touch->default_grab; @@ -456,6 +572,8 @@ weston_touch_destroy(struct weston_touch *touch) { /* XXX: What about touch->resource_list? */ + wl_list_remove(&touch->focus_view_listener.link); + wl_list_remove(&touch->focus_resource_listener.link); free(touch); } @@ -479,7 +597,7 @@ seat_send_updated_caps(struct weston_seat *seat) WL_EXPORT void weston_pointer_set_focus(struct weston_pointer *pointer, - struct weston_surface *surface, + struct weston_view *view, wl_fixed_t sx, wl_fixed_t sy) { struct weston_keyboard *kbd = pointer->seat->keyboard; @@ -487,27 +605,33 @@ weston_pointer_set_focus(struct weston_pointer *pointer, struct wl_display *display = pointer->seat->compositor->wl_display; uint32_t serial; struct wl_list *focus_resource_list; + int different_surface = 0; + + if ((!pointer->focus && view) || + (pointer->focus && !view) || + (pointer->focus && pointer->focus->surface != view->surface)) + different_surface = 1; focus_resource_list = &pointer->focus_resource_list; - if (!wl_list_empty(focus_resource_list) && pointer->focus != surface) { + if (!wl_list_empty(focus_resource_list) && different_surface) { serial = wl_display_next_serial(display); wl_resource_for_each(resource, focus_resource_list) { wl_pointer_send_leave(resource, serial, - pointer->focus->resource); + pointer->focus->surface->resource); } move_resources(&pointer->resource_list, focus_resource_list); } - if (find_resource_for_surface(&pointer->resource_list, surface) && - pointer->focus != surface) { + if (find_resource_for_view(&pointer->resource_list, view) && + different_surface) { struct wl_client *surface_client = - wl_resource_get_client(surface->resource); + wl_resource_get_client(view->surface->resource); serial = wl_display_next_serial(display); - if (kbd && kbd->focus != pointer->focus) + if (kbd && kbd->focus != view->surface) send_modifiers_to_client_in_list(surface_client, &kbd->resource_list, serial, @@ -520,14 +644,25 @@ weston_pointer_set_focus(struct weston_pointer *pointer, wl_resource_for_each(resource, focus_resource_list) { wl_pointer_send_enter(resource, serial, - surface->resource, + view->surface->resource, sx, sy); } pointer->focus_serial = serial; } - pointer->focus = surface; + wl_list_remove(&pointer->focus_view_listener.link); + wl_list_init(&pointer->focus_view_listener.link); + wl_list_remove(&pointer->focus_resource_listener.link); + wl_list_init(&pointer->focus_resource_listener.link); + if (view) + wl_signal_add(&view->destroy_signal, &pointer->focus_view_listener); + if (view && view->surface->resource) + wl_resource_add_destroy_listener(view->surface->resource, + &pointer->focus_resource_listener); + + pointer->focus = view; + pointer->focus_view_listener.notify = pointer_focus_view_destroyed; wl_signal_emit(&pointer->focus_signal, pointer); } @@ -584,6 +719,12 @@ weston_keyboard_set_focus(struct weston_keyboard *keyboard, keyboard->focus_serial = serial; } + wl_list_remove(&keyboard->focus_resource_listener.link); + wl_list_init(&keyboard->focus_resource_listener.link); + if (surface && surface->resource) + wl_resource_add_destroy_listener(surface->resource, + &keyboard->focus_resource_listener); + keyboard->focus = surface; wl_signal_emit(&keyboard->focus_signal, keyboard); } @@ -651,6 +792,28 @@ weston_touch_cancel_grab(struct weston_touch *touch) touch->grab->interface->cancel(touch->grab); } +static void +weston_pointer_clamp_for_output(struct weston_pointer *pointer, + struct weston_output *output, + wl_fixed_t *fx, wl_fixed_t *fy) +{ + int x, y; + + x = wl_fixed_to_int(*fx); + y = wl_fixed_to_int(*fy); + + if (x < output->x) + *fx = wl_fixed_from_int(output->x); + else if (x >= output->x + output->width) + *fx = wl_fixed_from_int(output->x + + output->width - 1); + if (y < output->y) + *fy = wl_fixed_from_int(output->y); + else if (y >= output->y + output->height) + *fy = wl_fixed_from_int(output->y + + output->height - 1); +} + WL_EXPORT void weston_pointer_clamp(struct weston_pointer *pointer, wl_fixed_t *fx, wl_fixed_t *fy) { @@ -677,27 +840,14 @@ weston_pointer_clamp(struct weston_pointer *pointer, wl_fixed_t *fx, wl_fixed_t if (!prev) prev = pointer->seat->output; - if (prev && !valid) { - if (x < prev->x) - *fx = wl_fixed_from_int(prev->x); - else if (x >= prev->x + prev->width) - *fx = wl_fixed_from_int(prev->x + - prev->width - 1); - if (y < prev->y) - *fy = wl_fixed_from_int(prev->y); - else if (y >= prev->y + prev->height) - *fy = wl_fixed_from_int(prev->y + - prev->height - 1); - } + if (prev && !valid) + weston_pointer_clamp_for_output(pointer, prev, fx, fy); } /* Takes absolute values */ -static void -move_pointer(struct weston_seat *seat, wl_fixed_t x, wl_fixed_t y) +WL_EXPORT void +weston_pointer_move(struct weston_pointer *pointer, wl_fixed_t x, wl_fixed_t y) { - struct weston_compositor *ec = seat->compositor; - struct weston_pointer *pointer = seat->pointer; - struct weston_output *output; int32_t ix, iy; weston_pointer_clamp (pointer, &x, &y); @@ -708,18 +858,54 @@ move_pointer(struct weston_seat *seat, wl_fixed_t x, wl_fixed_t y) ix = wl_fixed_to_int(x); iy = wl_fixed_to_int(y); - wl_list_for_each(output, &ec->output_list, link) - if (output->zoom.active && - pixman_region32_contains_point(&output->region, - ix, iy, NULL)) - weston_output_update_zoom(output, ZOOM_FOCUS_POINTER); - if (pointer->sprite) { - weston_surface_set_position(pointer->sprite, - ix - pointer->hotspot_x, - iy - pointer->hotspot_y); - weston_surface_schedule_repaint(pointer->sprite); + weston_view_set_position(pointer->sprite, + ix - pointer->hotspot_x, + iy - pointer->hotspot_y); + weston_view_schedule_repaint(pointer->sprite); } + + pointer->grab->interface->focus(pointer->grab); + wl_signal_emit(&pointer->motion_signal, pointer); +} + +/** Verify if the pointer is in a valid position and move it if it isn't. + */ +WL_EXPORT void +weston_pointer_verify(struct weston_pointer *pointer) +{ + struct weston_compositor *ec = pointer->seat->compositor; + struct weston_output *output, *closest = NULL; + int x, y, distance, min = INT_MAX; + wl_fixed_t fx, fy; + + x = wl_fixed_to_int(pointer->x); + y = wl_fixed_to_int(pointer->y); + + wl_list_for_each(output, &ec->output_list, link) { + if (pixman_region32_contains_point(&output->region, + x, y, NULL)) + return; + + /* Aproximante the distance from the pointer to the center of + * the output. */ + distance = abs(output->x + output->width / 2 - x) + + abs(output->y + output->height / 2 - y); + if (distance < min) { + min = distance; + closest = output; + } + } + + /* Nothing to do if there's no output left. */ + if (!closest) + return; + + fx = pointer->x; + fy = pointer->y; + + weston_pointer_clamp_for_output(pointer, closest, &fx, &fy); + weston_pointer_move(pointer, fx, fy); } WL_EXPORT void @@ -730,11 +916,43 @@ notify_motion(struct weston_seat *seat, struct weston_pointer *pointer = seat->pointer; weston_compositor_wake(ec); + pointer->grab->interface->motion(pointer->grab, time, pointer->x + dx, pointer->y + dy); +} - move_pointer(seat, pointer->x + dx, pointer->y + dy); +static void +run_modifier_bindings(struct weston_seat *seat, uint32_t old, uint32_t new) +{ + struct weston_compositor *compositor = seat->compositor; + struct weston_keyboard *keyboard = seat->keyboard; + uint32_t diff; + unsigned int i; + struct { + uint32_t xkb; + enum weston_keyboard_modifier weston; + } mods[] = { + { keyboard->xkb_info->ctrl_mod, MODIFIER_CTRL }, + { keyboard->xkb_info->alt_mod, MODIFIER_ALT }, + { keyboard->xkb_info->super_mod, MODIFIER_SUPER }, + { keyboard->xkb_info->shift_mod, MODIFIER_SHIFT }, + }; + + diff = new & ~old; + for (i = 0; i < ARRAY_LENGTH(mods); i++) { + if (diff & (1 << mods[i].xkb)) + weston_compositor_run_modifier_binding(compositor, + seat, + mods[i].weston, + WL_KEYBOARD_KEY_STATE_PRESSED); + } - pointer->grab->interface->focus(pointer->grab); - pointer->grab->interface->motion(pointer->grab, time); + diff = old & ~new; + for (i = 0; i < ARRAY_LENGTH(mods); i++) { + if (diff & (1 << mods[i].xkb)) + weston_compositor_run_modifier_binding(compositor, + seat, + mods[i].weston, + WL_KEYBOARD_KEY_STATE_RELEASED); + } } WL_EXPORT void @@ -745,11 +963,7 @@ notify_motion_absolute(struct weston_seat *seat, struct weston_pointer *pointer = seat->pointer; weston_compositor_wake(ec); - - move_pointer(seat, x, y); - - pointer->grab->interface->focus(pointer->grab); - pointer->grab->interface->motion(pointer->grab, time); + pointer->grab->interface->motion(pointer->grab, time, x, y); } WL_EXPORT void @@ -845,13 +1059,13 @@ notify_modifiers(struct weston_seat *seat, uint32_t serial) /* Serialize and update our internal state, checking to see if it's * different to the previous state. */ - mods_depressed = xkb_state_serialize_mods(seat->xkb_state.state, + mods_depressed = xkb_state_serialize_mods(keyboard->xkb_state.state, XKB_STATE_DEPRESSED); - mods_latched = xkb_state_serialize_mods(seat->xkb_state.state, + mods_latched = xkb_state_serialize_mods(keyboard->xkb_state.state, XKB_STATE_LATCHED); - mods_locked = xkb_state_serialize_mods(seat->xkb_state.state, + mods_locked = xkb_state_serialize_mods(keyboard->xkb_state.state, XKB_STATE_LOCKED); - group = xkb_state_serialize_group(seat->xkb_state.state, + group = xkb_state_serialize_group(keyboard->xkb_state.state, XKB_STATE_EFFECTIVE); if (mods_depressed != seat->keyboard->modifiers.mods_depressed || @@ -860,6 +1074,9 @@ notify_modifiers(struct weston_seat *seat, uint32_t serial) group != seat->keyboard->modifiers.group) changed = 1; + run_modifier_bindings(seat, seat->keyboard->modifiers.mods_depressed, + mods_depressed); + seat->keyboard->modifiers.mods_depressed = mods_depressed; seat->keyboard->modifiers.mods_latched = mods_latched; seat->keyboard->modifiers.mods_locked = mods_locked; @@ -868,28 +1085,28 @@ notify_modifiers(struct weston_seat *seat, uint32_t serial) /* And update the modifier_state for bindings. */ mods_lookup = mods_depressed | mods_latched; seat->modifier_state = 0; - if (mods_lookup & (1 << seat->xkb_info->ctrl_mod)) + if (mods_lookup & (1 << keyboard->xkb_info->ctrl_mod)) seat->modifier_state |= MODIFIER_CTRL; - if (mods_lookup & (1 << seat->xkb_info->alt_mod)) + if (mods_lookup & (1 << keyboard->xkb_info->alt_mod)) seat->modifier_state |= MODIFIER_ALT; - if (mods_lookup & (1 << seat->xkb_info->super_mod)) + if (mods_lookup & (1 << keyboard->xkb_info->super_mod)) seat->modifier_state |= MODIFIER_SUPER; - if (mods_lookup & (1 << seat->xkb_info->shift_mod)) + if (mods_lookup & (1 << keyboard->xkb_info->shift_mod)) seat->modifier_state |= MODIFIER_SHIFT; /* Finally, notify the compositor that LEDs have changed. */ - if (xkb_state_led_index_is_active(seat->xkb_state.state, - seat->xkb_info->num_led)) + if (xkb_state_led_index_is_active(keyboard->xkb_state.state, + keyboard->xkb_info->num_led)) leds |= LED_NUM_LOCK; - if (xkb_state_led_index_is_active(seat->xkb_state.state, - seat->xkb_info->caps_led)) + if (xkb_state_led_index_is_active(keyboard->xkb_state.state, + keyboard->xkb_info->caps_led)) leds |= LED_CAPS_LOCK; - if (xkb_state_led_index_is_active(seat->xkb_state.state, - seat->xkb_info->scroll_led)) + if (xkb_state_led_index_is_active(keyboard->xkb_state.state, + keyboard->xkb_info->scroll_led)) leds |= LED_SCROLL_LOCK; - if (leds != seat->xkb_state.leds && seat->led_update) + if (leds != keyboard->xkb_state.leds && seat->led_update) seat->led_update(seat, leds); - seat->xkb_state.leds = leds; + keyboard->xkb_state.leds = leds; if (changed) { grab->interface->modifiers(grab, @@ -905,6 +1122,7 @@ static void update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key, enum wl_keyboard_key_state state) { + struct weston_keyboard *keyboard = seat->keyboard; enum xkb_key_direction direction; /* Keyboard modifiers don't exist in raw keyboard mode */ @@ -918,10 +1136,91 @@ update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key, /* Offset the keycode by 8, as the evdev XKB rules reflect X's * broken keycode system, which starts at 8. */ - xkb_state_update_key(seat->xkb_state.state, key + 8, direction); + xkb_state_update_key(keyboard->xkb_state.state, key + 8, direction); notify_modifiers(seat, serial); } + +static void +send_keymap(struct wl_resource *resource, struct weston_xkb_info *xkb_info) +{ + wl_keyboard_send_keymap(resource, + WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, + xkb_info->keymap_fd, + xkb_info->keymap_size); +} + +static void +send_modifiers(struct wl_resource *resource, uint32_t serial, struct weston_keyboard *keyboard) +{ + wl_keyboard_send_modifiers(resource, serial, + keyboard->modifiers.mods_depressed, + keyboard->modifiers.mods_latched, + keyboard->modifiers.mods_locked, + keyboard->modifiers.group); +} + +static struct weston_xkb_info * +weston_xkb_info_create(struct xkb_keymap *keymap); + +static void +update_keymap(struct weston_seat *seat) +{ + struct weston_keyboard *keyboard = seat->keyboard; + struct wl_resource *resource; + struct weston_xkb_info *xkb_info; + struct xkb_state *state; + xkb_mod_mask_t latched_mods; + xkb_mod_mask_t locked_mods; + + xkb_info = weston_xkb_info_create(keyboard->pending_keymap); + + xkb_keymap_unref(keyboard->pending_keymap); + keyboard->pending_keymap = NULL; + + if (!xkb_info) { + weston_log("failed to create XKB info\n"); + return; + } + + state = xkb_state_new(xkb_info->keymap); + if (!state) { + weston_log("failed to initialise XKB state\n"); + weston_xkb_info_destroy(xkb_info); + return; + } + + latched_mods = xkb_state_serialize_mods(keyboard->xkb_state.state, + XKB_STATE_MODS_LATCHED); + locked_mods = xkb_state_serialize_mods(keyboard->xkb_state.state, + XKB_STATE_MODS_LOCKED); + xkb_state_update_mask(state, + 0, /* depressed */ + latched_mods, + locked_mods, + 0, 0, 0); + + weston_xkb_info_destroy(keyboard->xkb_info); + keyboard->xkb_info = xkb_info; + + xkb_state_unref(keyboard->xkb_state.state); + keyboard->xkb_state.state = state; + + wl_resource_for_each(resource, &seat->keyboard->resource_list) + send_keymap(resource, xkb_info); + wl_resource_for_each(resource, &seat->keyboard->focus_resource_list) + send_keymap(resource, xkb_info); + + notify_modifiers(seat, wl_display_next_serial(seat->compositor->wl_display)); + + if (!latched_mods && !locked_mods) + return; + + wl_resource_for_each(resource, &seat->keyboard->resource_list) + send_modifiers(resource, wl_display_get_serial(seat->compositor->wl_display), seat->keyboard); + wl_resource_for_each(resource, &seat->keyboard->focus_resource_list) + send_modifiers(resource, wl_display_get_serial(seat->compositor->wl_display), seat->keyboard); +} #else WL_EXPORT void notify_modifiers(struct weston_seat *seat, uint32_t serial) @@ -933,6 +1232,11 @@ update_modifier_state(struct weston_seat *seat, uint32_t serial, uint32_t key, enum wl_keyboard_key_state state) { } + +static void +update_keymap(struct weston_seat *seat) +{ +} #endif WL_EXPORT void @@ -942,8 +1246,7 @@ notify_key(struct weston_seat *seat, uint32_t time, uint32_t key, { struct weston_compositor *compositor = seat->compositor; struct weston_keyboard *keyboard = seat->keyboard; - struct weston_surface *focus = - (struct weston_surface *) keyboard->focus; + struct weston_surface *focus = keyboard->focus; struct weston_keyboard_grab *grab = keyboard->grab; uint32_t serial = wl_display_next_serial(compositor->wl_display); uint32_t *k, *end; @@ -983,6 +1286,10 @@ notify_key(struct weston_seat *seat, uint32_t time, uint32_t key, grab->interface->key(grab, time, key, state); + if (keyboard->pending_keymap && + keyboard->keys.size == 0) + update_keymap(seat); + if (update_state == STATE_UPDATE_AUTOMATIC) { update_modifier_state(seat, wl_display_get_serial(compositor->wl_display), @@ -995,13 +1302,9 @@ WL_EXPORT void notify_pointer_focus(struct weston_seat *seat, struct weston_output *output, wl_fixed_t x, wl_fixed_t y) { - struct weston_compositor *compositor = seat->compositor; - if (output) { - move_pointer(seat, x, y); - compositor->focus = 1; + weston_pointer_move(seat->pointer, x, y); } else { - compositor->focus = 0; /* FIXME: We should call weston_pointer_set_focus(seat, * NULL) here, but somehow that breaks re-entry... */ } @@ -1077,31 +1380,44 @@ notify_keyboard_focus_out(struct weston_seat *seat) weston_keyboard_set_focus(keyboard, NULL); weston_keyboard_cancel_grab(keyboard); + if (seat->pointer) + weston_pointer_cancel_grab(seat->pointer); } WL_EXPORT void -weston_touch_set_focus(struct weston_seat *seat, struct weston_surface *surface) +weston_touch_set_focus(struct weston_seat *seat, struct weston_view *view) { struct wl_list *focus_resource_list; focus_resource_list = &seat->touch->focus_resource_list; - if (seat->touch->focus == surface) + if (view && seat->touch->focus && + seat->touch->focus->surface == view->surface) { + seat->touch->focus = view; return; + } + + wl_list_remove(&seat->touch->focus_resource_listener.link); + wl_list_init(&seat->touch->focus_resource_listener.link); + wl_list_remove(&seat->touch->focus_view_listener.link); + wl_list_init(&seat->touch->focus_view_listener.link); if (!wl_list_empty(focus_resource_list)) { move_resources(&seat->touch->resource_list, focus_resource_list); } - if (surface) { + if (view) { struct wl_client *surface_client = - wl_resource_get_client(surface->resource); + wl_resource_get_client(view->surface->resource); move_resources_for_client(focus_resource_list, &seat->touch->resource_list, surface_client); + wl_resource_add_destroy_listener(view->surface->resource, + &seat->touch->focus_resource_listener); + wl_signal_add(&view->destroy_signal, &seat->touch->focus_view_listener); } - seat->touch->focus = surface; + seat->touch->focus = view; } /** @@ -1119,7 +1435,7 @@ notify_touch(struct weston_seat *seat, uint32_t time, int touch_id, struct weston_compositor *ec = seat->compositor; struct weston_touch *touch = seat->touch; struct weston_touch_grab *grab = touch->grab; - struct weston_surface *es; + struct weston_view *ev; wl_fixed_t sx, sy; /* Update grab's global coordinates. */ @@ -1132,28 +1448,28 @@ notify_touch(struct weston_seat *seat, uint32_t time, int touch_id, case WL_TOUCH_DOWN: weston_compositor_idle_inhibit(ec); - seat->num_tp++; + touch->num_tp++; - /* the first finger down picks the surface, and all further go - * to that surface for the remainder of the touch session i.e. + /* the first finger down picks the view, and all further go + * to that view for the remainder of the touch session i.e. * until all touch points are up again. */ - if (seat->num_tp == 1) { - es = weston_compositor_pick_surface(ec, x, y, &sx, &sy); - weston_touch_set_focus(seat, es); + if (touch->num_tp == 1) { + ev = weston_compositor_pick_view(ec, x, y, &sx, &sy); + weston_touch_set_focus(seat, ev); } else if (touch->focus) { - es = (struct weston_surface *) touch->focus; - weston_surface_from_global_fixed(es, x, y, &sx, &sy); + ev = touch->focus; + weston_view_from_global_fixed(ev, x, y, &sx, &sy); } else { /* Unexpected condition: We have non-initial touch but * there is no focused surface. */ weston_log("touch event received with %d points down" - "but no surface focused\n", seat->num_tp); + "but no surface focused\n", touch->num_tp); return; } grab->interface->down(grab, time, touch_id, sx, sy); - if (seat->num_tp == 1) { + if (touch->num_tp == 1) { touch->grab_serial = wl_display_get_serial(ec->wl_display); touch->grab_touch_id = touch_id; @@ -1164,19 +1480,27 @@ notify_touch(struct weston_seat *seat, uint32_t time, int touch_id, break; case WL_TOUCH_MOTION: - es = (struct weston_surface *) touch->focus; - if (!es) + ev = touch->focus; + if (!ev) break; - weston_surface_from_global_fixed(es, x, y, &sx, &sy); + weston_view_from_global_fixed(ev, x, y, &sx, &sy); grab->interface->motion(grab, time, touch_id, sx, sy); break; case WL_TOUCH_UP: + if (touch->num_tp == 0) { + /* This can happen if we start out with one or + * more fingers on the touch screen, in which + * case we didn't get the corresponding down + * event. */ + weston_log("unmatched touch up event\n"); + break; + } weston_compositor_idle_release(ec); - seat->num_tp--; + touch->num_tp--; grab->interface->up(grab, time, touch_id); - if (seat->num_tp == 0) + if (touch->num_tp == 0) weston_touch_set_focus(seat, NULL); break; } @@ -1186,15 +1510,15 @@ notify_touch(struct weston_seat *seat, uint32_t time, int touch_id, static void pointer_cursor_surface_configure(struct weston_surface *es, - int32_t dx, int32_t dy, int32_t width, int32_t height) + int32_t dx, int32_t dy) { struct weston_pointer *pointer = es->configure_private; int x, y; - if (width == 0) + if (es->width == 0) return; - assert(es == pointer->sprite); + assert(es == pointer->sprite->surface); pointer->hotspot_x -= dx; pointer->hotspot_y -= dy; @@ -1202,14 +1526,14 @@ pointer_cursor_surface_configure(struct weston_surface *es, x = wl_fixed_to_int(pointer->x) - pointer->hotspot_x; y = wl_fixed_to_int(pointer->y) - pointer->hotspot_y; - weston_surface_configure(pointer->sprite, x, y, width, height); + weston_view_set_position(pointer->sprite, x, y); empty_region(&es->pending.input); if (!weston_surface_is_mapped(es)) { - wl_list_insert(&es->compositor->cursor_layer.surface_list, - &es->layer_link); - weston_surface_update_transform(es); + wl_list_insert(&es->compositor->cursor_layer.view_list, + &pointer->sprite->layer_link); + weston_view_update_transform(pointer->sprite); } } @@ -1226,17 +1550,17 @@ pointer_set_cursor(struct wl_client *client, struct wl_resource *resource, if (pointer->focus == NULL) return; - /* pointer->focus->resource can be NULL. Surfaces like the + /* pointer->focus->surface->resource can be NULL. Surfaces like the black_surface used in shell.c for fullscreen don't have a resource, but can still have focus */ - if (pointer->focus->resource == NULL) + if (pointer->focus->surface->resource == NULL) return; - if (wl_resource_get_client(pointer->focus->resource) != client) + if (wl_resource_get_client(pointer->focus->surface->resource) != client) return; if (pointer->focus_serial - serial > UINT32_MAX / 2) return; - if (surface && surface != pointer->sprite) { + if (surface && pointer->sprite && surface != pointer->sprite->surface) { if (surface->configure) { wl_resource_post_error(surface->resource, WL_DISPLAY_ERROR_INVALID_OBJECT, @@ -1257,13 +1581,12 @@ pointer_set_cursor(struct wl_client *client, struct wl_resource *resource, surface->configure = pointer_cursor_surface_configure; surface->configure_private = pointer; - pointer->sprite = surface; + pointer->sprite = weston_view_create(surface); pointer->hotspot_x = x; pointer->hotspot_y = y; if (surface->buffer_ref.buffer) - pointer_cursor_surface_configure(surface, 0, 0, weston_surface_buffer_width(surface), - weston_surface_buffer_height(surface)); + pointer_cursor_surface_configure(surface, 0, 0); } static void @@ -1301,24 +1624,21 @@ seat_get_pointer(struct wl_client *client, struct wl_resource *resource, wl_resource_set_implementation(cr, &pointer_interface, seat->pointer, unbind_resource); - if (seat->pointer->focus && seat->pointer->focus->resource && - wl_resource_get_client(seat->pointer->focus->resource) == client) { - struct weston_surface *surface; + if (seat->pointer->focus && seat->pointer->focus->surface->resource && + wl_resource_get_client(seat->pointer->focus->surface->resource) == client) { wl_fixed_t sx, sy; - surface = (struct weston_surface *) seat->pointer->focus; - weston_surface_from_global_fixed(surface, - seat->pointer->x, - seat->pointer->y, - &sx, - &sy); + weston_view_from_global_fixed(seat->pointer->focus, + seat->pointer->x, + seat->pointer->y, + &sx, &sy); wl_list_remove(wl_resource_get_link(cr)); wl_list_insert(&seat->pointer->focus_resource_list, wl_resource_get_link(cr)); wl_pointer_send_enter(cr, seat->pointer->focus_serial, - surface->resource, + seat->pointer->focus->surface->resource, sx, sy); } } @@ -1339,12 +1659,14 @@ should_send_modifiers_to_client(struct weston_seat *seat, { if (seat->keyboard && seat->keyboard->focus && + seat->keyboard->focus->resource && wl_resource_get_client(seat->keyboard->focus->resource) == client) return 1; if (seat->pointer && seat->pointer->focus && - wl_resource_get_client(seat->pointer->focus->resource) == client) + seat->pointer->focus->surface->resource && + wl_resource_get_client(seat->pointer->focus->surface->resource) == client) return 1; return 0; @@ -1355,6 +1677,7 @@ seat_get_keyboard(struct wl_client *client, struct wl_resource *resource, uint32_t id) { struct weston_seat *seat = wl_resource_get_user_data(resource); + struct weston_keyboard *keyboard = seat->keyboard; struct wl_resource *cr; if (!seat->keyboard) @@ -1376,8 +1699,8 @@ seat_get_keyboard(struct wl_client *client, struct wl_resource *resource, if (seat->compositor->use_xkbcommon) { wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, - seat->xkb_info->keymap_fd, - seat->xkb_info->keymap_size); + keyboard->xkb_info->keymap_fd, + keyboard->xkb_info->keymap_size); } else { int null_fd = open("/dev/null", O_RDONLY); wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP, @@ -1441,7 +1764,7 @@ seat_get_touch(struct wl_client *client, struct wl_resource *resource, } if (seat->touch->focus && - wl_resource_get_client(seat->touch->focus->resource) == client) { + wl_resource_get_client(seat->touch->focus->surface->resource) == client) { wl_list_insert(&seat->touch->resource_list, wl_resource_get_link(cr)); } else { @@ -1639,6 +1962,7 @@ weston_compositor_build_global_keymap(struct weston_compositor *ec) } ec->xkb_info = weston_xkb_info_create(keymap); + xkb_keymap_unref(keymap); if (ec->xkb_info == NULL) return -1; @@ -1658,6 +1982,24 @@ weston_compositor_xkb_destroy(struct weston_compositor *ec) } #endif +WL_EXPORT void +weston_seat_update_keymap(struct weston_seat *seat, struct xkb_keymap *keymap) +{ + if (!seat->keyboard || !keymap) + return; + +#ifdef ENABLE_XKBCOMMON + if (!seat->compositor->use_xkbcommon) + return; + + xkb_keymap_unref(seat->keyboard->pending_keymap); + seat->keyboard->pending_keymap = xkb_keymap_ref(keymap); + + if (seat->keyboard->keys.size == 0) + update_keymap(seat); +#endif +} + WL_EXPORT int weston_seat_init_keyboard(struct weston_seat *seat, struct xkb_keymap *keymap) { @@ -1670,44 +2012,67 @@ weston_seat_init_keyboard(struct weston_seat *seat, struct xkb_keymap *keymap) return 0; } + keyboard = weston_keyboard_create(); + if (keyboard == NULL) { + weston_log("failed to allocate weston keyboard struct\n"); + return -1; + } + + seat->keyboard = keyboard; + seat->keyboard_device_count = 1; + keyboard->seat = seat; + #ifdef ENABLE_XKBCOMMON if (seat->compositor->use_xkbcommon) { if (keymap != NULL) { - seat->xkb_info = weston_xkb_info_create(keymap); - if (seat->xkb_info == NULL) + keyboard->xkb_info = weston_xkb_info_create(keymap); + if (keyboard->xkb_info == NULL) return -1; } else { if (weston_compositor_build_global_keymap(seat->compositor) < 0) return -1; - seat->xkb_info = seat->compositor->xkb_info; - seat->xkb_info->ref_count++; + keyboard->xkb_info = seat->compositor->xkb_info; + keyboard->xkb_info->ref_count++; } - seat->xkb_state.state = xkb_state_new(seat->xkb_info->keymap); - if (seat->xkb_state.state == NULL) { + keyboard->xkb_state.state = xkb_state_new(keyboard->xkb_info->keymap); + if (keyboard->xkb_state.state == NULL) { weston_log("failed to initialise XKB state\n"); return -1; } - seat->xkb_state.leds = 0; + keyboard->xkb_state.leds = 0; } #endif - keyboard = weston_keyboard_create(); - if (keyboard == NULL) { - weston_log("failed to allocate weston keyboard struct\n"); - return -1; - } - - seat->keyboard = keyboard; - seat->keyboard_device_count = 1; - keyboard->seat = seat; - seat_send_updated_caps(seat); return 0; } +static void +weston_keyboard_reset_state(struct weston_keyboard *keyboard) +{ + struct weston_seat *seat = keyboard->seat; + struct xkb_state *state; + +#ifdef ENABLE_XKBCOMMON + if (seat->compositor->use_xkbcommon) { + state = xkb_state_new(keyboard->xkb_info->keymap); + if (!state) { + weston_log("failed to reset XKB state\n"); + return; + } + xkb_state_unref(keyboard->xkb_state.state); + keyboard->xkb_state.state = state; + + keyboard->xkb_state.leds = 0; + } +#endif + + seat->modifier_state = 0; +} + WL_EXPORT void weston_seat_release_keyboard(struct weston_seat *seat) { @@ -1715,6 +2080,7 @@ weston_seat_release_keyboard(struct weston_seat *seat) if (seat->keyboard_device_count == 0) { weston_keyboard_set_focus(seat->keyboard, NULL); weston_keyboard_cancel_grab(seat->keyboard); + weston_keyboard_reset_state(seat->keyboard); seat_send_updated_caps(seat); } } @@ -1731,7 +2097,7 @@ weston_seat_init_pointer(struct weston_seat *seat) return; } - pointer = weston_pointer_create(); + pointer = weston_pointer_create(seat); if (pointer == NULL) return; @@ -1757,6 +2123,7 @@ weston_seat_release_pointer(struct weston_seat *seat) if (pointer->sprite) pointer_unmap_sprite(pointer); + weston_pointer_reset_state(pointer); seat_send_updated_caps(seat); } } @@ -1791,6 +2158,7 @@ weston_seat_release_touch(struct weston_seat *seat) if (seat->touch_device_count == 0) { weston_touch_set_focus(seat, NULL); weston_touch_cancel_grab(seat->touch); + weston_touch_reset_state(seat->touch); seat_send_updated_caps(seat); } } @@ -1812,7 +2180,6 @@ weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec, seat->compositor = ec; seat->modifier_state = 0; - seat->num_tp = 0; seat->seat_name = strdup(seat_name); wl_list_insert(ec->seat_list.prev, &seat->link); @@ -1827,15 +2194,6 @@ weston_seat_release(struct weston_seat *seat) { wl_list_remove(&seat->link); -#ifdef ENABLE_XKBCOMMON - if (seat->compositor->use_xkbcommon) { - if (seat->xkb_state.state != NULL) - xkb_state_unref(seat->xkb_state.state); - if (seat->xkb_info) - weston_xkb_info_destroy(seat->xkb_info); - } -#endif - if (seat->pointer) weston_pointer_destroy(seat->pointer); if (seat->keyboard) diff --git a/src/launcher-util.c b/src/launcher-util.c index 4ce06c4f..1b63458c 100644 --- a/src/launcher-util.c +++ b/src/launcher-util.c @@ -40,12 +40,9 @@ #include #include -#ifdef BUILD_DRM_COMPOSITOR -#include -#endif - #include "compositor.h" #include "launcher-util.h" +#include "logind-util.h" #include "weston-launch.h" #define DRM_MAJOR 226 @@ -54,42 +51,55 @@ #define KDSKBMUTE 0x4B51 #endif -union cmsg_data { unsigned char b[4]; int fd; }; - -struct weston_launcher { - struct weston_compositor *compositor; - int fd; - struct wl_event_source *source; +#ifdef HAVE_LIBDRM - int kb_mode, tty, drm_fd; - struct wl_event_source *vt_source; -}; +#include -#ifdef BUILD_DRM_COMPOSITOR -static int -drm_drop_master(int drm_fd) -{ - return drmDropMaster(drm_fd); -} -static int -drm_set_master(int drm_fd) -{ - return drmSetMaster(drm_fd); -} -static int -drm_is_master(int drm_fd) +static inline int +is_drm_master(int drm_fd) { drm_magic_t magic; return drmGetMagic(drm_fd, &magic) == 0 && drmAuthMagic(drm_fd, magic) == 0; } + #else -static int drm_drop_master(int drm_fd) {return 0;} -static int drm_set_master(int drm_fd) {return 0;} -static int drm_is_master(int drm_fd) {return 1;} + +static inline int +drmDropMaster(int drm_fd) +{ + return 0; +} + +static inline int +drmSetMaster(int drm_fd) +{ + return 0; +} + +static inline int +is_drm_master(int drm_fd) +{ + return 0; +} + #endif + +union cmsg_data { unsigned char b[4]; int fd; }; + +struct weston_launcher { + struct weston_compositor *compositor; + struct weston_logind *logind; + struct wl_event_loop *loop; + int fd; + struct wl_event_source *source; + + int kb_mode, tty, drm_fd; + struct wl_event_source *vt_source; +}; + int weston_launcher_open(struct weston_launcher *launcher, const char *path, int flags) @@ -104,6 +114,9 @@ weston_launcher_open(struct weston_launcher *launcher, struct weston_launcher_open *message; struct stat s; + if (launcher->logind) + return weston_logind_open(launcher->logind, path, flags); + if (launcher->fd == -1) { fd = open(path, flags | O_CLOEXEC); if (fd == -1) @@ -116,7 +129,7 @@ weston_launcher_open(struct weston_launcher *launcher, if (major(s.st_rdev) == DRM_MAJOR) { launcher->drm_fd = fd; - if (!drm_is_master(fd)) { + if (!is_drm_master(fd)) { weston_log("drm fd not master\n"); close(fd); return -1; @@ -173,11 +186,23 @@ weston_launcher_open(struct weston_launcher *launcher, return data->fd; } +void +weston_launcher_close(struct weston_launcher *launcher, int fd) +{ + if (launcher->logind) + return weston_logind_close(launcher->logind, fd); + + close(fd); +} + void weston_launcher_restore(struct weston_launcher *launcher) { struct vt_mode mode = { 0 }; + if (launcher->logind) + return weston_logind_restore(launcher->logind); + if (ioctl(launcher->tty, KDSKBMUTE, 0) && ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode)) weston_log("failed to restore kb mode: %m\n"); @@ -188,7 +213,7 @@ weston_launcher_restore(struct weston_launcher *launcher) /* We have to drop master before we switch the VT back in * VT_AUTO, so we don't risk switching to a VT with another * display server, that will then fail to set drm master. */ - drm_drop_master(launcher->drm_fd); + drmDropMaster(launcher->drm_fd); mode.mode = VT_AUTO; if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) @@ -242,11 +267,11 @@ vt_handler(int signal_number, void *data) if (compositor->session_active) { compositor->session_active = 0; wl_signal_emit(&compositor->session_signal, compositor); - drm_drop_master(launcher->drm_fd); + drmDropMaster(launcher->drm_fd); ioctl(launcher->tty, VT_RELDISP, 1); } else { ioctl(launcher->tty, VT_RELDISP, VT_ACKACQ); - drm_set_master(launcher->drm_fd); + drmSetMaster(launcher->drm_fd); compositor->session_active = 1; wl_signal_emit(&compositor->session_signal, compositor); } @@ -341,19 +366,25 @@ setup_tty(struct weston_launcher *launcher, int tty) int weston_launcher_activate_vt(struct weston_launcher *launcher, int vt) { + if (launcher->logind) + return weston_logind_activate_vt(launcher->logind, vt); + return ioctl(launcher->tty, VT_ACTIVATE, vt); } struct weston_launcher * -weston_launcher_connect(struct weston_compositor *compositor, int tty) +weston_launcher_connect(struct weston_compositor *compositor, int tty, + const char *seat_id) { struct weston_launcher *launcher; struct wl_event_loop *loop; + int r; launcher = malloc(sizeof *launcher); if (launcher == NULL) return NULL; + launcher->logind = NULL; launcher->compositor = compositor; launcher->drm_fd = -1; launcher->fd = weston_environment_get_fd("WESTON_LAUNCHER_SOCK"); @@ -368,14 +399,21 @@ weston_launcher_connect(struct weston_compositor *compositor, int tty) free(launcher); return NULL; } - } else if (geteuid() == 0) { - if (setup_tty(launcher, tty) == -1) { - free(launcher); - return NULL; - } } else { - free(launcher); - return NULL; + r = weston_logind_connect(&launcher->logind, compositor, + seat_id, tty); + if (r < 0) { + launcher->logind = NULL; + if (geteuid() == 0) { + if (setup_tty(launcher, tty) == -1) { + free(launcher); + return NULL; + } + } else { + free(launcher); + return NULL; + } + } } return launcher; @@ -384,7 +422,9 @@ weston_launcher_connect(struct weston_compositor *compositor, int tty) void weston_launcher_destroy(struct weston_launcher *launcher) { - if (launcher->fd != -1) { + if (launcher->logind) { + weston_logind_destroy(launcher->logind); + } else if (launcher->fd != -1) { close(launcher->fd); wl_event_source_remove(launcher->source); } else { @@ -392,6 +432,8 @@ weston_launcher_destroy(struct weston_launcher *launcher) wl_event_source_remove(launcher->vt_source); } - close(launcher->tty); + if (launcher->tty >= 0) + close(launcher->tty); + free(launcher); } diff --git a/src/launcher-util.h b/src/launcher-util.h index 3e7ceb59..d5b2fc98 100644 --- a/src/launcher-util.h +++ b/src/launcher-util.h @@ -30,7 +30,8 @@ struct weston_launcher; struct weston_launcher * -weston_launcher_connect(struct weston_compositor *compositor, int tty); +weston_launcher_connect(struct weston_compositor *compositor, int tty, + const char *seat_id); void weston_launcher_destroy(struct weston_launcher *launcher); @@ -39,6 +40,9 @@ int weston_launcher_open(struct weston_launcher *launcher, const char *path, int flags); +void +weston_launcher_close(struct weston_launcher *launcher, int fd); + int weston_launcher_activate_vt(struct weston_launcher *launcher, int vt); diff --git a/src/logind-util.c b/src/logind-util.c new file mode 100644 index 00000000..40d811b4 --- /dev/null +++ b/src/logind-util.c @@ -0,0 +1,940 @@ +/* + * Copyright © 2013 David Herrmann + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compositor.h" +#include "dbus.h" +#include "logind-util.h" + +#define DRM_MAJOR 226 + +#ifndef KDSKBMUTE +#define KDSKBMUTE 0x4B51 +#endif + +struct weston_logind { + struct weston_compositor *compositor; + char *seat; + char *sid; + unsigned int vtnr; + int vt; + int kb_mode; + int sfd; + struct wl_event_source *sfd_source; + + DBusConnection *dbus; + struct wl_event_source *dbus_ctx; + char *spath; + DBusPendingCall *pending_active; +}; + +static int +weston_logind_take_device(struct weston_logind *wl, uint32_t major, + uint32_t minor, bool *paused_out) +{ + DBusMessage *m, *reply; + bool b; + int r, fd; + dbus_bool_t paused; + + m = dbus_message_new_method_call("org.freedesktop.login1", + wl->spath, + "org.freedesktop.login1.Session", + "TakeDevice"); + if (!m) + return -ENOMEM; + + b = dbus_message_append_args(m, + DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_INVALID); + if (!b) { + r = -ENOMEM; + goto err_unref; + } + + reply = dbus_connection_send_with_reply_and_block(wl->dbus, m, + -1, NULL); + if (!reply) { + r = -ENODEV; + goto err_unref; + } + + b = dbus_message_get_args(reply, NULL, + DBUS_TYPE_UNIX_FD, &fd, + DBUS_TYPE_BOOLEAN, &paused, + DBUS_TYPE_INVALID); + if (!b) { + r = -ENODEV; + goto err_reply; + } + + r = fd; + if (paused_out) + *paused_out = paused; + +err_reply: + dbus_message_unref(reply); +err_unref: + dbus_message_unref(m); + return r; +} + +static void +weston_logind_release_device(struct weston_logind *wl, uint32_t major, + uint32_t minor) +{ + DBusMessage *m; + bool b; + + m = dbus_message_new_method_call("org.freedesktop.login1", + wl->spath, + "org.freedesktop.login1.Session", + "ReleaseDevice"); + if (m) { + b = dbus_message_append_args(m, + DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_INVALID); + if (b) + dbus_connection_send(wl->dbus, m, NULL); + dbus_message_unref(m); + } +} + +static void +weston_logind_pause_device_complete(struct weston_logind *wl, uint32_t major, + uint32_t minor) +{ + DBusMessage *m; + bool b; + + m = dbus_message_new_method_call("org.freedesktop.login1", + wl->spath, + "org.freedesktop.login1.Session", + "PauseDeviceComplete"); + if (m) { + b = dbus_message_append_args(m, + DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_INVALID); + if (b) + dbus_connection_send(wl->dbus, m, NULL); + dbus_message_unref(m); + } +} + +WL_EXPORT int +weston_logind_open(struct weston_logind *wl, const char *path, + int flags) +{ + struct stat st; + int fl, r, fd; + + r = stat(path, &st); + if (r < 0) + return -1; + if (!S_ISCHR(st.st_mode)) { + errno = ENODEV; + return -1; + } + + fd = weston_logind_take_device(wl, major(st.st_rdev), + minor(st.st_rdev), NULL); + if (fd < 0) + return fd; + + /* Compared to weston_launcher_open() we cannot specify the open-mode + * directly. Instead, logind passes us an fd with sane default modes. + * For DRM and evdev this means O_RDWR | O_CLOEXEC. If we want + * something else, we need to change it afterwards. We currently + * only support dropping FD_CLOEXEC and setting O_NONBLOCK. Changing + * access-modes is not possible so accept whatever logind passes us. */ + + fl = fcntl(fd, F_GETFL); + if (fl < 0) { + r = -errno; + goto err_close; + } + + if (flags & O_NONBLOCK) + fl |= O_NONBLOCK; + + r = fcntl(fd, F_SETFL, fl); + if (r < 0) { + r = -errno; + goto err_close; + } + + fl = fcntl(fd, F_GETFD); + if (fl < 0) { + r = -errno; + goto err_close; + } + + if (!(flags & O_CLOEXEC)) + fl &= ~FD_CLOEXEC; + + r = fcntl(fd, F_SETFD, fl); + if (r < 0) { + r = -errno; + goto err_close; + } + + return fd; + +err_close: + close(fd); + weston_logind_release_device(wl, major(st.st_rdev), + minor(st.st_rdev)); + errno = -r; + return -1; +} + +WL_EXPORT void +weston_logind_close(struct weston_logind *wl, int fd) +{ + struct stat st; + int r; + + r = fstat(fd, &st); + if (r < 0) { + weston_log("logind: cannot fstat fd: %m\n"); + return; + } + + if (!S_ISCHR(st.st_mode)) { + weston_log("logind: invalid device passed\n"); + return; + } + + weston_logind_release_device(wl, major(st.st_rdev), + minor(st.st_rdev)); +} + +WL_EXPORT void +weston_logind_restore(struct weston_logind *wl) +{ + struct vt_mode mode = { 0 }; + + ioctl(wl->vt, KDSETMODE, KD_TEXT); + ioctl(wl->vt, KDSKBMUTE, 0); + ioctl(wl->vt, KDSKBMODE, wl->kb_mode); + mode.mode = VT_AUTO; + ioctl(wl->vt, VT_SETMODE, &mode); +} + +WL_EXPORT int +weston_logind_activate_vt(struct weston_logind *wl, int vt) +{ + int r; + + r = ioctl(wl->vt, VT_ACTIVATE, vt); + if (r < 0) + return -1; + + return 0; +} + +static void +weston_logind_set_active(struct weston_logind *wl, bool active) +{ + if (!wl->compositor->session_active == !active) + return; + + wl->compositor->session_active = active; + + wl_signal_emit(&wl->compositor->session_signal, + wl->compositor); +} + +static void +get_active_cb(DBusPendingCall *pending, void *data) +{ + struct weston_logind *wl = data; + DBusMessage *m; + DBusMessageIter iter, sub; + int type; + dbus_bool_t b; + + dbus_pending_call_unref(wl->pending_active); + wl->pending_active = NULL; + + m = dbus_pending_call_steal_reply(pending); + if (!m) + return; + + type = dbus_message_get_type(m); + if (type != DBUS_MESSAGE_TYPE_METHOD_RETURN) + goto err_unref; + + if (!dbus_message_iter_init(m, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) + goto err_unref; + + dbus_message_iter_recurse(&iter, &sub); + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN) + goto err_unref; + + dbus_message_iter_get_basic(&sub, &b); + if (!b) + weston_logind_set_active(wl, false); + +err_unref: + dbus_message_unref(m); +} + +static void +weston_logind_get_active(struct weston_logind *wl) +{ + DBusPendingCall *pending; + DBusMessage *m; + bool b; + const char *iface, *name; + + m = dbus_message_new_method_call("org.freedesktop.login1", + wl->spath, + "org.freedesktop.DBus.Properties", + "Get"); + if (!m) + return; + + iface = "org.freedesktop.login1.Session"; + name = "Active"; + b = dbus_message_append_args(m, + DBUS_TYPE_STRING, &iface, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID); + if (!b) + goto err_unref; + + b = dbus_connection_send_with_reply(wl->dbus, m, &pending, -1); + if (!b) + goto err_unref; + + b = dbus_pending_call_set_notify(pending, get_active_cb, wl, NULL); + if (!b) { + dbus_pending_call_cancel(pending); + dbus_pending_call_unref(pending); + goto err_unref; + } + + if (wl->pending_active) { + dbus_pending_call_cancel(wl->pending_active); + dbus_pending_call_unref(wl->pending_active); + } + wl->pending_active = pending; + return; + +err_unref: + dbus_message_unref(m); +} + +static void +disconnected_dbus(struct weston_logind *wl) +{ + weston_log("logind: dbus connection lost, exiting..\n"); + weston_logind_restore(wl); + exit(-1); +} + +static void +session_removed(struct weston_logind *wl, DBusMessage *m) +{ + const char *name, *obj; + bool r; + + r = dbus_message_get_args(m, NULL, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_OBJECT_PATH, &obj, + DBUS_TYPE_INVALID); + if (!r) { + weston_log("logind: cannot parse SessionRemoved dbus signal\n"); + return; + } + + if (!strcmp(name, wl->sid)) { + weston_log("logind: our session got closed, exiting..\n"); + weston_logind_restore(wl); + exit(-1); + } +} + +static void +property_changed(struct weston_logind *wl, DBusMessage *m) +{ + DBusMessageIter iter, sub, entry; + const char *interface, *name; + dbus_bool_t b; + + if (!dbus_message_iter_init(m, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + goto error; + + dbus_message_iter_get_basic(&iter, &interface); + + if (!dbus_message_iter_next(&iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) + goto error; + + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY) { + dbus_message_iter_recurse(&sub, &entry); + + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) + goto error; + + dbus_message_iter_get_basic(&entry, &name); + if (!dbus_message_iter_next(&entry)) + goto error; + + if (!strcmp(name, "Active")) { + if (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_BOOLEAN) { + dbus_message_iter_get_basic(&entry, &b); + if (!b) + weston_logind_set_active(wl, false); + return; + } + } + + dbus_message_iter_next(&sub); + } + + if (!dbus_message_iter_next(&iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) + goto error; + + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) { + dbus_message_iter_get_basic(&sub, &name); + + if (!strcmp(name, "Active")) { + weston_logind_get_active(wl); + return; + } + + dbus_message_iter_next(&sub); + } + + return; + +error: + weston_log("logind: cannot parse PropertiesChanged dbus signal\n"); +} + +static void +device_paused(struct weston_logind *wl, DBusMessage *m) +{ + bool r; + const char *type; + uint32_t major, minor; + + r = dbus_message_get_args(m, NULL, + DBUS_TYPE_UINT32, &major, + DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_STRING, &type, + DBUS_TYPE_INVALID); + if (!r) { + weston_log("logind: cannot parse PauseDevice dbus signal\n"); + return; + } + + /* "pause" means synchronous pausing. Acknowledge it unconditionally + * as we support asynchronous device shutdowns, anyway. + * "force" means asynchronous pausing. + * "gone" means the device is gone. We handle it the same as "force" as + * a following udev event will be caught, too. + * + * If it's our main DRM device, tell the compositor to go asleep. */ + + if (!strcmp(type, "pause")) + weston_logind_pause_device_complete(wl, major, minor); + + if (major == DRM_MAJOR) + weston_logind_set_active(wl, false); +} + +static void +device_resumed(struct weston_logind *wl, DBusMessage *m) +{ + bool r; + uint32_t major; + + r = dbus_message_get_args(m, NULL, + DBUS_TYPE_UINT32, &major, + /*DBUS_TYPE_UINT32, &minor, + DBUS_TYPE_UNIX_FD, &fd,*/ + DBUS_TYPE_INVALID); + if (!r) { + weston_log("logind: cannot parse ResumeDevice dbus signal\n"); + return; + } + + /* DeviceResumed messages provide us a new file-descriptor for + * resumed devices. For DRM devices it's the same as before, for evdev + * devices it's a new open-file. As we reopen evdev devices, anyway, + * there is no need for us to handle this event for evdev. For DRM, we + * notify the compositor to wake up. */ + + if (major == DRM_MAJOR) + weston_logind_set_active(wl, true); +} + +static DBusHandlerResult +filter_dbus(DBusConnection *c, DBusMessage *m, void *data) +{ + struct weston_logind *wl = data; + + if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) { + disconnected_dbus(wl); + } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Manager", + "SessionRemoved")) { + session_removed(wl, m); + } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties", + "PropertiesChanged")) { + property_changed(wl, m); + } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session", + "PauseDevice")) { + device_paused(wl, m); + } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session", + "ResumeDevice")) { + device_resumed(wl, m); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static int +weston_logind_setup_dbus(struct weston_logind *wl) +{ + bool b; + int r; + + r = asprintf(&wl->spath, "/org/freedesktop/login1/session/%s", + wl->sid); + if (r < 0) + return -ENOMEM; + + b = dbus_connection_add_filter(wl->dbus, filter_dbus, wl, NULL); + if (!b) { + weston_log("logind: cannot add dbus filter\n"); + r = -ENOMEM; + goto err_spath; + } + + r = weston_dbus_add_match_signal(wl->dbus, + "org.freedesktop.login1", + "org.freedesktop.login1.Manager", + "SessionRemoved", + "/org/freedesktop/login1"); + if (r < 0) { + weston_log("logind: cannot add dbus match\n"); + goto err_spath; + } + + r = weston_dbus_add_match_signal(wl->dbus, + "org.freedesktop.login1", + "org.freedesktop.login1.Session", + "PauseDevice", + wl->spath); + if (r < 0) { + weston_log("logind: cannot add dbus match\n"); + goto err_spath; + } + + r = weston_dbus_add_match_signal(wl->dbus, + "org.freedesktop.login1", + "org.freedesktop.login1.Session", + "ResumeDevice", + wl->spath); + if (r < 0) { + weston_log("logind: cannot add dbus match\n"); + goto err_spath; + } + + r = weston_dbus_add_match_signal(wl->dbus, + "org.freedesktop.login1", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + wl->spath); + if (r < 0) { + weston_log("logind: cannot add dbus match\n"); + goto err_spath; + } + + return 0; + +err_spath: + /* don't remove any dbus-match as the connection is closed, anyway */ + free(wl->spath); + return r; +} + +static void +weston_logind_destroy_dbus(struct weston_logind *wl) +{ + /* don't remove any dbus-match as the connection is closed, anyway */ + free(wl->spath); +} + +static int +weston_logind_take_control(struct weston_logind *wl) +{ + DBusError err; + DBusMessage *m, *reply; + dbus_bool_t force; + bool b; + int r; + + dbus_error_init(&err); + + m = dbus_message_new_method_call("org.freedesktop.login1", + wl->spath, + "org.freedesktop.login1.Session", + "TakeControl"); + if (!m) + return -ENOMEM; + + force = false; + b = dbus_message_append_args(m, + DBUS_TYPE_BOOLEAN, &force, + DBUS_TYPE_INVALID); + if (!b) { + r = -ENOMEM; + goto err_unref; + } + + reply = dbus_connection_send_with_reply_and_block(wl->dbus, + m, -1, &err); + if (!reply) { + if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD)) + weston_log("logind: old systemd version detected\n"); + else + weston_log("logind: cannot take control over session %s\n", wl->sid); + + dbus_error_free(&err); + r = -EIO; + goto err_unref; + } + + dbus_message_unref(reply); + dbus_message_unref(m); + return 0; + +err_unref: + dbus_message_unref(m); + return r; +} + +static void +weston_logind_release_control(struct weston_logind *wl) +{ + DBusMessage *m; + + m = dbus_message_new_method_call("org.freedesktop.login1", + wl->spath, + "org.freedesktop.login1.Session", + "ReleaseControl"); + if (m) { + dbus_connection_send(wl->dbus, m, NULL); + dbus_message_unref(m); + } +} + +static int +signal_event(int fd, uint32_t mask, void *data) +{ + struct weston_logind *wl = data; + struct signalfd_siginfo sig; + + if (read(fd, &sig, sizeof sig) != sizeof sig) { + weston_log("logind: cannot read signalfd: %m\n"); + return 0; + } + + switch (sig.ssi_signo) { + case SIGUSR1: + ioctl(wl->vt, VT_RELDISP, 1); + break; + case SIGUSR2: + ioctl(wl->vt, VT_RELDISP, VT_ACKACQ); + break; + } + + return 0; +} + +static int +weston_logind_setup_vt(struct weston_logind *wl) +{ + struct stat st; + char buf[64]; + struct vt_mode mode = { 0 }; + int r; + sigset_t mask; + struct wl_event_loop *loop; + + snprintf(buf, sizeof(buf), "/dev/tty%d", wl->vtnr); + buf[sizeof(buf) - 1] = 0; + + wl->vt = open(buf, O_RDWR|O_CLOEXEC|O_NONBLOCK); + + if (wl->vt < 0) { + r = -errno; + weston_log("logind: cannot open VT %s: %m\n", buf); + return r; + } + + if (fstat(wl->vt, &st) == -1 || + major(st.st_rdev) != TTY_MAJOR || minor(st.st_rdev) <= 0 || + minor(st.st_rdev) >= 64) { + r = -EINVAL; + weston_log("logind: TTY %s is no virtual terminal\n", buf); + goto err_close; + } + + /*r = setsid(); + if (r < 0 && errno != EPERM) { + r = -errno; + weston_log("logind: setsid() failed: %m\n"); + goto err_close; + } + + r = ioctl(wl->vt, TIOCSCTTY, 0); + if (r < 0) + weston_log("logind: VT %s already in use\n", buf);*/ + + if (ioctl(wl->vt, KDGKBMODE, &wl->kb_mode) < 0) { + weston_log("logind: cannot read keyboard mode on %s: %m\n", + buf); + wl->kb_mode = K_UNICODE; + } else if (wl->kb_mode == K_OFF) { + wl->kb_mode = K_UNICODE; + } + + if (ioctl(wl->vt, KDSKBMUTE, 1) < 0 && + ioctl(wl->vt, KDSKBMODE, K_OFF) < 0) { + r = -errno; + weston_log("logind: cannot set K_OFF KB-mode on %s: %m\n", + buf); + goto err_close; + } + + if (ioctl(wl->vt, KDSETMODE, KD_GRAPHICS) < 0) { + r = -errno; + weston_log("logind: cannot set KD_GRAPHICS mode on %s: %m\n", + buf); + goto err_kbmode; + } + + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + sigaddset(&mask, SIGUSR2); + sigprocmask(SIG_BLOCK, &mask, NULL); + + wl->sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); + if (wl->sfd < 0) { + r = -errno; + weston_log("logind: cannot create signalfd: %m\n"); + goto err_mode; + } + + loop = wl_display_get_event_loop(wl->compositor->wl_display); + wl->sfd_source = wl_event_loop_add_fd(loop, wl->sfd, + WL_EVENT_READABLE, + signal_event, wl); + if (!wl->sfd_source) { + r = -errno; + weston_log("logind: cannot create signalfd source: %m\n"); + goto err_sfd; + } + + mode.mode = VT_PROCESS; + mode.relsig = SIGUSR1; + mode.acqsig = SIGUSR2; + if (ioctl(wl->vt, VT_SETMODE, &mode) < 0) { + r = -errno; + weston_log("logind: cannot take over VT: %m\n"); + goto err_sfd_source; + } + + weston_log("logind: using VT %s\n", buf); + return 0; + +err_sfd_source: + wl_event_source_remove(wl->sfd_source); +err_sfd: + close(wl->sfd); +err_mode: + ioctl(wl->vt, KDSETMODE, KD_TEXT); +err_kbmode: + ioctl(wl->vt, KDSKBMUTE, 0); + ioctl(wl->vt, KDSKBMODE, wl->kb_mode); +err_close: + close(wl->vt); + return r; +} + +static void +weston_logind_destroy_vt(struct weston_logind *wl) +{ + weston_logind_restore(wl); + wl_event_source_remove(wl->sfd_source); + close(wl->sfd); + close(wl->vt); +} + +WL_EXPORT int +weston_logind_connect(struct weston_logind **out, + struct weston_compositor *compositor, + const char *seat_id, int tty) +{ + struct weston_logind *wl; + struct wl_event_loop *loop; + char *t; + int r; + + wl = calloc(1, sizeof(*wl)); + if (!wl) { + r = -ENOMEM; + goto err_out; + } + + wl->compositor = compositor; + + wl->seat = strdup(seat_id); + if (!wl->seat) { + r = -ENOMEM; + goto err_wl; + } + + r = sd_pid_get_session(getpid(), &wl->sid); + if (r < 0) { + weston_log("logind: not running in a systemd session\n"); + goto err_seat; + } + + t = NULL; + r = sd_session_get_seat(wl->sid, &t); + if (r < 0) { + weston_log("logind: failed to get session seat\n"); + free(t); + goto err_session; + } else if (strcmp(seat_id, t)) { + weston_log("logind: weston's seat '%s' differs from session-seat '%s'\n", + seat_id, t); + r = -EINVAL; + free(t); + goto err_session; + } + free(t); + + r = weston_sd_session_get_vt(wl->sid, &wl->vtnr); + if (r < 0) { + weston_log("logind: session not running on a VT\n"); + goto err_session; + } else if (tty > 0 && wl->vtnr != (unsigned int )tty) { + weston_log("logind: requested VT --tty=%d differs from real session VT %u\n", + tty, wl->vtnr); + r = -EINVAL; + goto err_session; + } + + loop = wl_display_get_event_loop(compositor->wl_display); + r = weston_dbus_open(loop, DBUS_BUS_SYSTEM, &wl->dbus, &wl->dbus_ctx); + if (r < 0) { + weston_log("logind: cannot connect to system dbus\n"); + goto err_session; + } + + r = weston_logind_setup_dbus(wl); + if (r < 0) + goto err_dbus; + + r = weston_logind_take_control(wl); + if (r < 0) + goto err_dbus_cleanup; + + r = weston_logind_setup_vt(wl); + if (r < 0) + goto err_control; + + weston_log("logind: session control granted\n"); + *out = wl; + return 0; + +err_control: + weston_logind_release_control(wl); +err_dbus_cleanup: + weston_logind_destroy_dbus(wl); +err_dbus: + weston_dbus_close(wl->dbus, wl->dbus_ctx); +err_session: + free(wl->sid); +err_seat: + free(wl->seat); +err_wl: + free(wl); +err_out: + weston_log("logind: cannot setup systemd-logind helper (%d), using legacy fallback\n", r); + errno = -r; + return -1; +} + +WL_EXPORT void +weston_logind_destroy(struct weston_logind *wl) +{ + if (wl->pending_active) { + dbus_pending_call_cancel(wl->pending_active); + dbus_pending_call_unref(wl->pending_active); + } + + weston_logind_destroy_vt(wl); + weston_logind_release_control(wl); + weston_logind_destroy_dbus(wl); + weston_dbus_close(wl->dbus, wl->dbus_ctx); + free(wl->sid); + free(wl->seat); + free(wl); +} diff --git a/src/logind-util.h b/src/logind-util.h new file mode 100644 index 00000000..552395e7 --- /dev/null +++ b/src/logind-util.h @@ -0,0 +1,120 @@ +/* + * Copyright © 2013 David Herrmann + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "compositor.h" + +struct weston_logind; + +#if defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS) + +#include + +int +weston_logind_open(struct weston_logind *wl, const char *path, + int flags); + +void +weston_logind_close(struct weston_logind *wl, int fd); + +void +weston_logind_restore(struct weston_logind *wl); + +int +weston_logind_activate_vt(struct weston_logind *wl, int vt); + +int +weston_logind_connect(struct weston_logind **out, + struct weston_compositor *compositor, + const char *seat_id, int tty); + +void +weston_logind_destroy(struct weston_logind *wl); + +static inline int +weston_sd_session_get_vt(const char *sid, unsigned int *out) +{ +#ifdef HAVE_SYSTEMD_LOGIN_209 + return sd_session_get_vt(sid, out); +#else + int r; + char *tty; + + r = sd_session_get_tty(sid, &tty); + if (r < 0) + return r; + + r = sscanf(tty, "tty%u", out); + free(tty); + + if (r != 1) + return -EINVAL; + + return 0; +#endif +} + +#else /* defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS) */ + +static inline int +weston_logind_open(struct weston_logind *wl, const char *path, + int flags) +{ + return -ENOSYS; +} + +static inline void +weston_logind_close(struct weston_logind *wl, int fd) +{ +} + +static inline void +weston_logind_restore(struct weston_logind *wl) +{ +} + +static inline int +weston_logind_activate_vt(struct weston_logind *wl, int vt) +{ + return -ENOSYS; +} + +static inline int +weston_logind_connect(struct weston_logind **out, + struct weston_compositor *compositor, + const char *seat_id, int tty) +{ + return -ENOSYS; +} + +static inline void +weston_logind_destroy(struct weston_logind *wl) +{ +} + +#endif /* defined(HAVE_SYSTEMD_LOGIN) && defined(HAVE_DBUS) */ diff --git a/src/noop-renderer.c b/src/noop-renderer.c index 91659f58..ad750b5a 100644 --- a/src/noop-renderer.c +++ b/src/noop-renderer.c @@ -51,23 +51,12 @@ noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) { } -static int -noop_renderer_create_surface(struct weston_surface *surface) -{ - return 0; -} - static void noop_renderer_surface_set_color(struct weston_surface *surface, float red, float green, float blue, float alpha) { } -static void -noop_renderer_destroy_surface(struct weston_surface *surface) -{ -} - static void noop_renderer_destroy(struct weston_compositor *ec) { @@ -88,9 +77,7 @@ noop_renderer_init(struct weston_compositor *ec) renderer->repaint_output = noop_renderer_repaint_output; renderer->flush_damage = noop_renderer_flush_damage; renderer->attach = noop_renderer_attach; - renderer->create_surface = noop_renderer_create_surface; renderer->surface_set_color = noop_renderer_surface_set_color; - renderer->destroy_surface = noop_renderer_destroy_surface; renderer->destroy = noop_renderer_destroy; ec->renderer = renderer; diff --git a/src/pixman-renderer.c b/src/pixman-renderer.c index 987c5393..26f6f279 100644 --- a/src/pixman-renderer.c +++ b/src/pixman-renderer.c @@ -37,14 +37,24 @@ struct pixman_output_state { }; struct pixman_surface_state { + struct weston_surface *surface; + pixman_image_t *image; struct weston_buffer_reference buffer_ref; + + struct wl_listener buffer_destroy_listener; + struct wl_listener surface_destroy_listener; + struct wl_listener renderer_destroy_listener; }; struct pixman_renderer { struct weston_renderer base; + int repaint_debug; pixman_image_t *debug_color; + struct weston_binding *debug_binding; + + struct wl_signal destroy_signal; }; static inline struct pixman_output_state * @@ -53,9 +63,15 @@ get_output_state(struct weston_output *output) return (struct pixman_output_state *)output->renderer_state; } +static int +pixman_renderer_create_surface(struct weston_surface *surface); + static inline struct pixman_surface_state * get_surface_state(struct weston_surface *surface) { + if (!surface->renderer_state) + pixman_renderer_create_surface(surface); + return (struct pixman_surface_state *)surface->renderer_state; } @@ -111,128 +127,28 @@ pixman_renderer_read_pixels(struct weston_output *output, return 0; } -static void -box_scale(pixman_box32_t *dst, int scale) -{ - dst->x1 *= scale; - dst->x2 *= scale; - dst->y1 *= scale; - dst->y2 *= scale; -} - -static void -scale_region (pixman_region32_t *region, int scale) -{ - pixman_box32_t *rects, *scaled_rects; - int nrects, i; - - if (scale != 1) { - rects = pixman_region32_rectangles(region, &nrects); - scaled_rects = calloc(nrects, sizeof(pixman_box32_t)); - - for (i = 0; i < nrects; i++) { - scaled_rects[i] = rects[i]; - box_scale(&scaled_rects[i], scale); - } - pixman_region32_clear(region); - - pixman_region32_init_rects (region, scaled_rects, nrects); - free (scaled_rects); - } -} - -static void -transform_region (pixman_region32_t *region, int width, int height, enum wl_output_transform transform) -{ - pixman_box32_t *rects, *transformed_rects; - int nrects, i; - - if (transform == WL_OUTPUT_TRANSFORM_NORMAL) - return; - - rects = pixman_region32_rectangles(region, &nrects); - transformed_rects = calloc(nrects, sizeof(pixman_box32_t)); - - for (i = 0; i < nrects; i++) { - switch (transform) { - default: - case WL_OUTPUT_TRANSFORM_NORMAL: - transformed_rects[i].x1 = rects[i].x1; - transformed_rects[i].y1 = rects[i].y1; - transformed_rects[i].x2 = rects[i].x2; - transformed_rects[i].y2 = rects[i].y2; - break; - case WL_OUTPUT_TRANSFORM_90: - transformed_rects[i].x1 = height - rects[i].y2; - transformed_rects[i].y1 = rects[i].x1; - transformed_rects[i].x2 = height - rects[i].y1; - transformed_rects[i].y2 = rects[i].x2; - break; - case WL_OUTPUT_TRANSFORM_180: - transformed_rects[i].x1 = width - rects[i].x2; - transformed_rects[i].y1 = height - rects[i].y2; - transformed_rects[i].x2 = width - rects[i].x1; - transformed_rects[i].y2 = height - rects[i].y1; - break; - case WL_OUTPUT_TRANSFORM_270: - transformed_rects[i].x1 = rects[i].y1; - transformed_rects[i].y1 = width - rects[i].x2; - transformed_rects[i].x2 = rects[i].y2; - transformed_rects[i].y2 = width - rects[i].x1; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED: - transformed_rects[i].x1 = width - rects[i].x2; - transformed_rects[i].y1 = rects[i].y1; - transformed_rects[i].x2 = width - rects[i].x1; - transformed_rects[i].y2 = rects[i].y2; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - transformed_rects[i].x1 = height - rects[i].y2; - transformed_rects[i].y1 = width - rects[i].x2; - transformed_rects[i].x2 = height - rects[i].y1; - transformed_rects[i].y2 = width - rects[i].x1; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - transformed_rects[i].x1 = rects[i].x1; - transformed_rects[i].y1 = height - rects[i].y2; - transformed_rects[i].x2 = rects[i].x2; - transformed_rects[i].y2 = height - rects[i].y1; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - transformed_rects[i].x1 = rects[i].y1; - transformed_rects[i].y1 = rects[i].x1; - transformed_rects[i].x2 = rects[i].y2; - transformed_rects[i].y2 = rects[i].x2; - break; - } - } - pixman_region32_clear(region); - - pixman_region32_init_rects (region, transformed_rects, nrects); - free (transformed_rects); -} - static void region_global_to_output(struct weston_output *output, pixman_region32_t *region) { pixman_region32_translate(region, -output->x, -output->y); - transform_region (region, output->width, output->height, output->transform); - scale_region (region, output->current_scale); + weston_transformed_region(output->width, output->height, + output->transform, output->current_scale, + region, region); } #define D2F(v) pixman_double_to_fixed((double)v) static void -repaint_region(struct weston_surface *es, struct weston_output *output, +repaint_region(struct weston_view *ev, struct weston_output *output, pixman_region32_t *region, pixman_region32_t *surf_region, pixman_op_t pixman_op) { struct pixman_renderer *pr = (struct pixman_renderer *) output->compositor->renderer; - struct pixman_surface_state *ps = get_surface_state(es); + struct pixman_surface_state *ps = get_surface_state(ev->surface); struct pixman_output_state *po = get_output_state(output); pixman_region32_t final_region; - float surface_x, surface_y; + float view_x, view_y; pixman_transform_t transform; pixman_fixed_t fw, fh; @@ -246,11 +162,11 @@ repaint_region(struct weston_surface *es, struct weston_output *output, pixman_region32_copy(&final_region, surf_region); /* Convert from surface to global coordinates */ - if (!es->transform.enabled) { - pixman_region32_translate(&final_region, es->geometry.x, es->geometry.y); + if (!ev->transform.enabled) { + pixman_region32_translate(&final_region, ev->geometry.x, ev->geometry.y); } else { - weston_surface_to_global_float(es, 0, 0, &surface_x, &surface_y); - pixman_region32_translate(&final_region, (int)surface_x, (int)surface_y); + weston_view_to_global_float(ev, 0, 0, &view_x, &view_y); + pixman_region32_translate(&final_region, (int)view_x, (int)view_y); } /* We need to paint the intersection */ @@ -314,22 +230,22 @@ repaint_region(struct weston_surface *es, struct weston_output *output, pixman_double_to_fixed (output->x), pixman_double_to_fixed (output->y)); - if (es->transform.enabled) { + if (ev->transform.enabled) { /* Pixman supports only 2D transform matrix, but Weston uses 3D, * so we're omitting Z coordinate here */ pixman_transform_t surface_transform = {{ - { D2F(es->transform.matrix.d[0]), - D2F(es->transform.matrix.d[4]), - D2F(es->transform.matrix.d[12]), + { D2F(ev->transform.matrix.d[0]), + D2F(ev->transform.matrix.d[4]), + D2F(ev->transform.matrix.d[12]), }, - { D2F(es->transform.matrix.d[1]), - D2F(es->transform.matrix.d[5]), - D2F(es->transform.matrix.d[13]), + { D2F(ev->transform.matrix.d[1]), + D2F(ev->transform.matrix.d[5]), + D2F(ev->transform.matrix.d[13]), }, - { D2F(es->transform.matrix.d[3]), - D2F(es->transform.matrix.d[7]), - D2F(es->transform.matrix.d[15]), + { D2F(ev->transform.matrix.d[3]), + D2F(ev->transform.matrix.d[7]), + D2F(ev->transform.matrix.d[15]), } }}; @@ -337,15 +253,37 @@ repaint_region(struct weston_surface *es, struct weston_output *output, pixman_transform_multiply (&transform, &surface_transform, &transform); } else { pixman_transform_translate(&transform, NULL, - pixman_double_to_fixed ((double)-es->geometry.x), - pixman_double_to_fixed ((double)-es->geometry.y)); + pixman_double_to_fixed ((double)-ev->geometry.x), + pixman_double_to_fixed ((double)-ev->geometry.y)); } + if (ev->surface->buffer_viewport.viewport_set) { + double viewport_x, viewport_y, viewport_width, viewport_height; + double ratio_x, ratio_y; + + viewport_x = wl_fixed_to_double(ev->surface->buffer_viewport.src_x); + viewport_y = wl_fixed_to_double(ev->surface->buffer_viewport.src_y); + viewport_width = wl_fixed_to_double(ev->surface->buffer_viewport.src_width); + viewport_height = wl_fixed_to_double(ev->surface->buffer_viewport.src_height); + + ratio_x = viewport_width / ev->surface->buffer_viewport.dst_width; + ratio_y = viewport_height / ev->surface->buffer_viewport.dst_height; + + pixman_transform_scale(&transform, NULL, + pixman_double_to_fixed(ratio_x), + pixman_double_to_fixed(ratio_y)); + pixman_transform_translate(&transform, NULL, pixman_double_to_fixed(viewport_x), + pixman_double_to_fixed(viewport_y)); + } + + pixman_transform_scale(&transform, NULL, + pixman_double_to_fixed(ev->surface->buffer_viewport.scale), + pixman_double_to_fixed(ev->surface->buffer_viewport.scale)); - fw = pixman_int_to_fixed(es->geometry.width); - fh = pixman_int_to_fixed(es->geometry.height); + fw = pixman_int_to_fixed(pixman_image_get_width(ps->image)); + fh = pixman_int_to_fixed(pixman_image_get_height(ps->image)); - switch (es->buffer_transform) { + switch (ev->surface->buffer_viewport.transform) { case WL_OUTPUT_TRANSFORM_FLIPPED: case WL_OUTPUT_TRANSFORM_FLIPPED_90: case WL_OUTPUT_TRANSFORM_FLIPPED_180: @@ -357,7 +295,7 @@ repaint_region(struct weston_surface *es, struct weston_output *output, break; } - switch (es->buffer_transform) { + switch (ev->surface->buffer_viewport.transform) { default: case WL_OUTPUT_TRANSFORM_NORMAL: case WL_OUTPUT_TRANSFORM_FLIPPED: @@ -379,17 +317,16 @@ repaint_region(struct weston_surface *es, struct weston_output *output, break; } - pixman_transform_scale(&transform, NULL, - pixman_double_to_fixed ((double)es->buffer_scale), - pixman_double_to_fixed ((double)es->buffer_scale)); - pixman_image_set_transform(ps->image, &transform); - if (es->transform.enabled || output->current_scale != es->buffer_scale) + if (ev->transform.enabled || output->current_scale != ev->surface->buffer_viewport.scale) pixman_image_set_filter(ps->image, PIXMAN_FILTER_BILINEAR, NULL, 0); else pixman_image_set_filter(ps->image, PIXMAN_FILTER_NEAREST, NULL, 0); + if (ps->buffer_ref.buffer) + wl_shm_buffer_begin_access(ps->buffer_ref.buffer->shm_buffer); + pixman_image_composite32(pixman_op, ps->image, /* src */ NULL /* mask */, @@ -400,6 +337,9 @@ repaint_region(struct weston_surface *es, struct weston_output *output, pixman_image_get_width (po->shadow_image), /* width */ pixman_image_get_height (po->shadow_image) /* height */); + if (ps->buffer_ref.buffer) + wl_shm_buffer_end_access(ps->buffer_ref.buffer->shm_buffer); + if (pr->repaint_debug) pixman_image_composite32(PIXMAN_OP_OVER, pr->debug_color, /* src */ @@ -417,10 +357,10 @@ repaint_region(struct weston_surface *es, struct weston_output *output, } static void -draw_surface(struct weston_surface *es, struct weston_output *output, - pixman_region32_t *damage) /* in global coordinates */ +draw_view(struct weston_view *ev, struct weston_output *output, + pixman_region32_t *damage) /* in global coordinates */ { - struct pixman_surface_state *ps = get_surface_state(es); + struct pixman_surface_state *ps = get_surface_state(ev->surface); /* repaint bounding region in global coordinates: */ pixman_region32_t repaint; /* non-opaque region in surface coordinates: */ @@ -432,8 +372,8 @@ draw_surface(struct weston_surface *es, struct weston_output *output, pixman_region32_init(&repaint); pixman_region32_intersect(&repaint, - &es->transform.boundingbox, damage); - pixman_region32_subtract(&repaint, &repaint, &es->clip); + &ev->transform.boundingbox, damage); + pixman_region32_subtract(&repaint, &repaint, &ev->clip); if (!pixman_region32_not_empty(&repaint)) goto out; @@ -444,21 +384,21 @@ draw_surface(struct weston_surface *es, struct weston_output *output, } /* TODO: Implement repaint_region_complex() using pixman_composite_trapezoids() */ - if (es->transform.enabled && - es->transform.matrix.type != WESTON_MATRIX_TRANSFORM_TRANSLATE) { - repaint_region(es, output, &repaint, NULL, PIXMAN_OP_OVER); + if (ev->transform.enabled && + ev->transform.matrix.type != WESTON_MATRIX_TRANSFORM_TRANSLATE) { + repaint_region(ev, output, &repaint, NULL, PIXMAN_OP_OVER); } else { /* blended region is whole surface minus opaque region: */ pixman_region32_init_rect(&surface_blend, 0, 0, - es->geometry.width, es->geometry.height); - pixman_region32_subtract(&surface_blend, &surface_blend, &es->opaque); + ev->surface->width, ev->surface->height); + pixman_region32_subtract(&surface_blend, &surface_blend, &ev->surface->opaque); - if (pixman_region32_not_empty(&es->opaque)) { - repaint_region(es, output, &repaint, &es->opaque, PIXMAN_OP_SRC); + if (pixman_region32_not_empty(&ev->surface->opaque)) { + repaint_region(ev, output, &repaint, &ev->surface->opaque, PIXMAN_OP_SRC); } if (pixman_region32_not_empty(&surface_blend)) { - repaint_region(es, output, &repaint, &surface_blend, PIXMAN_OP_OVER); + repaint_region(ev, output, &repaint, &surface_blend, PIXMAN_OP_OVER); } pixman_region32_fini(&surface_blend); } @@ -471,11 +411,11 @@ static void repaint_surfaces(struct weston_output *output, pixman_region32_t *damage) { struct weston_compositor *compositor = output->compositor; - struct weston_surface *surface; + struct weston_view *view; - wl_list_for_each_reverse(surface, &compositor->surface_list, link) - if (surface->plane == &compositor->primary_plane) - draw_surface(surface, output, damage); + wl_list_for_each_reverse(view, &compositor->view_list, link) + if (view->plane == &compositor->primary_plane) + draw_view(view, output, damage); } static void @@ -528,6 +468,22 @@ pixman_renderer_flush_damage(struct weston_surface *surface) /* No-op for pixman renderer */ } +static void +buffer_state_handle_buffer_destroy(struct wl_listener *listener, void *data) +{ + struct pixman_surface_state *ps; + + ps = container_of(listener, struct pixman_surface_state, + buffer_destroy_listener); + + if (ps->image) { + pixman_image_unref(ps->image); + ps->image = NULL; + } + + ps->buffer_destroy_listener.notify = NULL; +} + static void pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) { @@ -537,6 +493,11 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) weston_buffer_reference(&ps->buffer_ref, buffer); + if (ps->buffer_destroy_listener.notify) { + wl_list_remove(&ps->buffer_destroy_listener.link); + ps->buffer_destroy_listener.notify = NULL; + } + if (ps->image) { pixman_image_unref(ps->image); ps->image = NULL; @@ -578,12 +539,60 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) buffer->width, buffer->height, wl_shm_buffer_get_data(shm_buffer), wl_shm_buffer_get_stride(shm_buffer)); + + ps->buffer_destroy_listener.notify = + buffer_state_handle_buffer_destroy; + wl_signal_add(&buffer->destroy_signal, + &ps->buffer_destroy_listener); +} + +static void +pixman_renderer_surface_state_destroy(struct pixman_surface_state *ps) +{ + wl_list_remove(&ps->surface_destroy_listener.link); + wl_list_remove(&ps->renderer_destroy_listener.link); + if (ps->buffer_destroy_listener.notify) { + wl_list_remove(&ps->buffer_destroy_listener.link); + ps->buffer_destroy_listener.notify = NULL; + } + + ps->surface->renderer_state = NULL; + + if (ps->image) { + pixman_image_unref(ps->image); + ps->image = NULL; + } + weston_buffer_reference(&ps->buffer_ref, NULL); + free(ps); +} + +static void +surface_state_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct pixman_surface_state *ps; + + ps = container_of(listener, struct pixman_surface_state, + surface_destroy_listener); + + pixman_renderer_surface_state_destroy(ps); +} + +static void +surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data) +{ + struct pixman_surface_state *ps; + + ps = container_of(listener, struct pixman_surface_state, + renderer_destroy_listener); + + pixman_renderer_surface_state_destroy(ps); } static int pixman_renderer_create_surface(struct weston_surface *surface) { struct pixman_surface_state *ps; + struct pixman_renderer *pr = get_renderer(surface->compositor); ps = calloc(1, sizeof *ps); if (!ps) @@ -591,6 +600,18 @@ pixman_renderer_create_surface(struct weston_surface *surface) surface->renderer_state = ps; + ps->surface = surface; + + ps->surface_destroy_listener.notify = + surface_state_handle_surface_destroy; + wl_signal_add(&surface->destroy_signal, + &ps->surface_destroy_listener); + + ps->renderer_destroy_listener.notify = + surface_state_handle_renderer_destroy; + wl_signal_add(&pr->destroy_signal, + &ps->renderer_destroy_listener); + return 0; } @@ -615,22 +636,14 @@ pixman_renderer_surface_set_color(struct weston_surface *es, } static void -pixman_renderer_destroy_surface(struct weston_surface *surface) +pixman_renderer_destroy(struct weston_compositor *ec) { - struct pixman_surface_state *ps = get_surface_state(surface); + struct pixman_renderer *pr = get_renderer(ec); - if (ps->image) { - pixman_image_unref(ps->image); - ps->image = NULL; - } - weston_buffer_reference(&ps->buffer_ref, NULL); - free(ps); -} + wl_signal_emit(&pr->destroy_signal, pr); + weston_binding_destroy(pr->debug_binding); + free(pr); -static void -pixman_renderer_destroy(struct weston_compositor *ec) -{ - free(ec->renderer); ec->renderer = NULL; } @@ -660,7 +673,7 @@ pixman_renderer_init(struct weston_compositor *ec) { struct pixman_renderer *renderer; - renderer = malloc(sizeof *renderer); + renderer = calloc(1, sizeof *renderer); if (renderer == NULL) return -1; @@ -670,19 +683,20 @@ pixman_renderer_init(struct weston_compositor *ec) renderer->base.repaint_output = pixman_renderer_repaint_output; renderer->base.flush_damage = pixman_renderer_flush_damage; renderer->base.attach = pixman_renderer_attach; - renderer->base.create_surface = pixman_renderer_create_surface; renderer->base.surface_set_color = pixman_renderer_surface_set_color; - renderer->base.destroy_surface = pixman_renderer_destroy_surface; renderer->base.destroy = pixman_renderer_destroy; ec->renderer = &renderer->base; ec->capabilities |= WESTON_CAP_ROTATION_ANY; ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP; - weston_compositor_add_debug_binding(ec, KEY_R, - debug_binding, ec); + renderer->debug_binding = + weston_compositor_add_debug_binding(ec, KEY_R, + debug_binding, ec); wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565); + wl_signal_init(&renderer->destroy_signal); + return 0; } diff --git a/src/rpi-bcm-stubs.h b/src/rpi-bcm-stubs.h index 703cd771..fa30570d 100644 --- a/src/rpi-bcm-stubs.h +++ b/src/rpi-bcm-stubs.h @@ -303,6 +303,19 @@ vc_dispmanx_get_handle_from_wl_buffer(struct wl_resource *_buffer) return DISPMANX_NO_HANDLE; } +static inline void +vc_dispmanx_set_wl_buffer_in_use(struct wl_resource *_buffer, int in_use) +{ +} + +static inline int +vc_dispmanx_element_set_opaque_rect(DISPMANX_UPDATE_HANDLE_T update, + DISPMANX_ELEMENT_HANDLE_T element, + const VC_RECT_T *opaque_rect) +{ + return -1; +} + /* from /opt/vc/include/EGL/eglplatform.h */ typedef struct { diff --git a/src/rpi-renderer.c b/src/rpi-renderer.c index a95cc604..c6b924c6 100644 --- a/src/rpi-renderer.c +++ b/src/rpi-renderer.c @@ -79,12 +79,16 @@ /* If we had a fully featured vc_dispmanx_resource_write_data()... */ /*#define HAVE_RESOURCE_WRITE_DATA_RECT 1*/ +/* If we had a vc_dispmanx_element_set_opaque_rect()... */ +/*#define HAVE_ELEMENT_SET_OPAQUE_RECT 1*/ + struct rpi_resource { DISPMANX_RESOURCE_HANDLE_T handle; int width; int height; /* height of the image (valid pixel data) */ int stride; /* bytes */ int buffer_height; /* height of the buffer */ + int enable_opaque_regions; VC_IMAGE_TYPE_T ifmt; }; @@ -104,15 +108,11 @@ enum buffer_type { struct rpir_surface { struct weston_surface *surface; - /* If link is empty, the surface is guaranteed to not be on screen, - * i.e. updates removing Elements have completed. - */ - struct wl_list link; - - DISPMANX_ELEMENT_HANDLE_T handle; - int layer; + struct wl_list views; + int visible_views; int need_swap; int single_buffer; + int enable_opaque_regions; struct rpi_resource resources[2]; struct rpi_resource *front; @@ -125,6 +125,24 @@ struct rpir_surface { struct weston_buffer_reference buffer_ref; enum buffer_type buffer_type; + + struct wl_listener surface_destroy_listener; +}; + +struct rpir_view { + struct rpir_surface *surface; + struct wl_list surface_link; + struct weston_view *view; + + /* If link is empty, the view is guaranteed to not be on screen, + * i.e. updates removing Elements have completed. + */ + struct wl_list link; + + DISPMANX_ELEMENT_HANDLE_T handle; + int layer; + + struct wl_listener view_destroy_listener; }; struct rpir_output { @@ -134,10 +152,10 @@ struct rpir_output { struct weston_matrix matrix; /* all Elements currently on screen */ - struct wl_list surface_list; /* struct rpir_surface::link */ + struct wl_list view_list; /* struct rpir_surface::link */ /* Elements just removed, waiting for update completion */ - struct wl_list surface_cleanup_list; /* struct rpir_surface::link */ + struct wl_list view_cleanup_list; /* struct rpir_surface::link */ struct rpi_resource capture_buffer; uint8_t *capture_data; @@ -147,6 +165,7 @@ struct rpi_renderer { struct weston_renderer base; int single_buffer; + int enable_opaque_regions; #ifdef ENABLE_EGL EGLDisplay egl_display; @@ -158,12 +177,33 @@ struct rpi_renderer { int has_bind_display; }; +static int +rpi_renderer_create_surface(struct weston_surface *base); + +static int +rpi_renderer_create_view(struct weston_view *base); + +static void +rpir_view_handle_view_destroy(struct wl_listener *listener, void *data); + static inline struct rpir_surface * to_rpir_surface(struct weston_surface *surface) { + if (!surface->renderer_state) + rpi_renderer_create_surface(surface); + return surface->renderer_state; } +static inline struct rpir_view * +to_rpir_view(struct weston_view *view) +{ + if (!view->renderer_state) + rpi_renderer_create_view(view); + + return view->renderer_state; +} + static inline struct rpir_output * to_rpir_output(struct weston_output *output) { @@ -272,9 +312,47 @@ shm_buffer_get_vc_format(struct wl_shm_buffer *buffer) } } +#ifndef HAVE_ELEMENT_SET_OPAQUE_RECT +static uint32_t * +apply_opaque_region(struct wl_shm_buffer *buffer, + pixman_region32_t *opaque_region) +{ + uint32_t *src, *dst; + int width; + int height; + int stride; + int x, y; + + width = wl_shm_buffer_get_width(buffer); + height = wl_shm_buffer_get_height(buffer); + stride = wl_shm_buffer_get_stride(buffer); + src = wl_shm_buffer_get_data(buffer); + + dst = malloc(height * stride); + if (dst == NULL) { + weston_log("rpi-renderer error: out of memory\n"); + return NULL; + } + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + int i = y * stride / 4 + x; + pixman_box32_t box; + if (pixman_region32_contains_point (opaque_region, x, y, &box)) { + dst[i] = src[i] | 0xff000000; + } else { + dst[i] = src[i]; + } + } + } + + return dst; +} +#endif + static int rpi_resource_update(struct rpi_resource *resource, struct weston_buffer *buffer, - pixman_region32_t *region) + pixman_region32_t *region, pixman_region32_t *opaque_region) { pixman_region32_t write_region; pixman_box32_t *r; @@ -298,6 +376,17 @@ rpi_resource_update(struct rpi_resource *resource, struct weston_buffer *buffer, stride = wl_shm_buffer_get_stride(buffer->shm_buffer); pixels = wl_shm_buffer_get_data(buffer->shm_buffer); +#ifndef HAVE_ELEMENT_SET_OPAQUE_RECT + if (pixman_region32_not_empty(opaque_region) && + wl_shm_buffer_get_format(buffer->shm_buffer) == WL_SHM_FORMAT_ARGB8888 && + resource->enable_opaque_regions) { + pixels = apply_opaque_region(buffer->shm_buffer, opaque_region); + + if (!pixels) + return -1; + } +#endif + ret = rpi_resource_realloc(resource, ifmt & ~PREMULT_ALPHA_FLAG, width, height, stride, height); if (ret < 0) @@ -308,6 +397,8 @@ rpi_resource_update(struct rpi_resource *resource, struct weston_buffer *buffer, pixman_region32_intersect(&write_region, &write_region, region); + wl_shm_buffer_begin_access(buffer->shm_buffer); + #ifdef HAVE_RESOURCE_WRITE_DATA_RECT /* XXX: Can this do a format conversion, so that scanout does not have to? */ r = pixman_region32_rectangles(&write_region, &n); @@ -342,11 +433,58 @@ rpi_resource_update(struct rpi_resource *resource, struct weston_buffer *buffer, width, r->y2 - r->y1, 0, r->y1, ret); #endif + wl_shm_buffer_end_access(buffer->shm_buffer); + pixman_region32_fini(&write_region); +#ifndef HAVE_ELEMENT_SET_OPAQUE_RECT + if (pixman_region32_not_empty(opaque_region) && + wl_shm_buffer_get_format(buffer->shm_buffer) == WL_SHM_FORMAT_ARGB8888 && + resource->enable_opaque_regions) + free(pixels); +#endif + return ret ? -1 : 0; } +static inline void +rpi_buffer_egl_lock(struct weston_buffer *buffer) +{ +#ifdef ENABLE_EGL + vc_dispmanx_set_wl_buffer_in_use(buffer->resource, 1); +#endif +} + +static inline void +rpi_buffer_egl_unlock(struct weston_buffer *buffer) +{ +#ifdef ENABLE_EGL + vc_dispmanx_set_wl_buffer_in_use(buffer->resource, 0); +#endif +} + +static void +rpir_egl_buffer_destroy(struct rpir_egl_buffer *egl_buffer) +{ + struct weston_buffer *buffer; + + if (egl_buffer == NULL) + return; + + buffer = egl_buffer->buffer_ref.buffer; + if (buffer == NULL) { + /* The client has already destroyed the wl_buffer, the + * compositor has the responsibility to delete the resource. + */ + vc_dispmanx_resource_delete(egl_buffer->resource_handle); + } else { + rpi_buffer_egl_unlock(buffer); + weston_buffer_reference(&egl_buffer->buffer_ref, NULL); + } + + free(egl_buffer); +} + static struct rpir_surface * rpir_surface_create(struct rpi_renderer *renderer) { @@ -356,9 +494,10 @@ rpir_surface_create(struct rpi_renderer *renderer) if (!surface) return NULL; - wl_list_init(&surface->link); + wl_list_init(&surface->views); + surface->visible_views = 0; surface->single_buffer = renderer->single_buffer; - surface->handle = DISPMANX_NO_HANDLE; + surface->enable_opaque_regions = renderer->enable_opaque_regions; rpi_resource_init(&surface->resources[0]); rpi_resource_init(&surface->resources[1]); surface->front = &surface->resources[0]; @@ -366,6 +505,10 @@ rpir_surface_create(struct rpi_renderer *renderer) surface->back = &surface->resources[0]; else surface->back = &surface->resources[1]; + + surface->front->enable_opaque_regions = renderer->enable_opaque_regions; + surface->back->enable_opaque_regions = renderer->enable_opaque_regions; + surface->buffer_type = BUFFER_TYPE_NULL; pixman_region32_init(&surface->prev_damage); @@ -376,33 +519,22 @@ rpir_surface_create(struct rpi_renderer *renderer) static void rpir_surface_destroy(struct rpir_surface *surface) { - wl_list_remove(&surface->link); - - if (surface->handle != DISPMANX_NO_HANDLE) + if (surface->visible_views) weston_log("ERROR rpi: destroying on-screen element\n"); + assert(wl_list_empty(&surface->views)); + + if (surface->surface) + surface->surface->renderer_state = NULL; + pixman_region32_fini(&surface->prev_damage); rpi_resource_release(&surface->resources[0]); rpi_resource_release(&surface->resources[1]); - DBG("rpir_surface %p destroyed (%u)\n", surface, surface->handle); - - if (surface->egl_back != NULL) { - weston_buffer_reference(&surface->egl_back->buffer_ref, NULL); - free(surface->egl_back); - surface->egl_back = NULL; - } + DBG("rpir_surface %p destroyed (%u)\n", surface, surface->visible_views); - if (surface->egl_front != NULL) { - weston_buffer_reference(&surface->egl_front->buffer_ref, NULL); - free(surface->egl_front); - surface->egl_front = NULL; - } - - if (surface->egl_old_front != NULL) { - weston_buffer_reference(&surface->egl_old_front->buffer_ref, NULL); - free(surface->egl_old_front); - surface->egl_old_front = NULL; - } + rpir_egl_buffer_destroy(surface->egl_back); + rpir_egl_buffer_destroy(surface->egl_front); + rpir_egl_buffer_destroy(surface->egl_old_front); free(surface); } @@ -422,11 +554,13 @@ rpir_surface_damage(struct rpir_surface *surface, struct weston_buffer *buffer, /* XXX: todo: if no surface->handle, update front buffer directly * to avoid creating a new back buffer */ if (surface->single_buffer) { - ret = rpi_resource_update(surface->front, buffer, damage); + ret = rpi_resource_update(surface->front, buffer, damage, + &surface->surface->opaque); } else { pixman_region32_init(&upload); pixman_region32_union(&upload, &surface->prev_damage, damage); - ret = rpi_resource_update(surface->back, buffer, &upload); + ret = rpi_resource_update(surface->back, buffer, &upload, + &surface->surface->opaque); pixman_region32_fini(&upload); } @@ -436,6 +570,46 @@ rpir_surface_damage(struct rpir_surface *surface, struct weston_buffer *buffer, return ret; } +static struct rpir_view * +rpir_view_create(struct rpir_surface *surface) +{ + struct rpir_view *view; + + view = calloc(1, sizeof *view); + if (!view) + return NULL; + + view->surface = surface; + wl_list_insert(&surface->views, &view->surface_link); + + wl_list_init(&view->link); + view->handle = DISPMANX_NO_HANDLE; + + return view; +} + +static void +rpir_view_destroy(struct rpir_view *view) +{ + wl_list_remove(&view->link); + + if (view->handle != DISPMANX_NO_HANDLE) { + view->surface->visible_views--; + weston_log("ERROR rpi: destroying on-screen element\n"); + } + + if (view->view) + view->view->renderer_state = NULL; + + wl_list_remove(&view->surface_link); + if (wl_list_empty(&view->surface->views) && view->surface->surface == NULL) + rpir_surface_destroy(view->surface); + + DBG("rpir_view %p destroyed (%d)\n", view, view->handle); + + free(view); +} + static void matrix_type_str(struct weston_matrix *matrix, char *buf, int len) { @@ -495,13 +669,13 @@ warn_bad_matrix(struct weston_matrix *total, struct weston_matrix *output, /*#define SURFACE_TRANSFORM */ static int -rpir_surface_compute_rects(struct rpir_surface *surface, - VC_RECT_T *src_rect, VC_RECT_T *dst_rect, - VC_IMAGE_TRANSFORM_T *flipmask) +rpir_view_compute_rects(struct rpir_view *view, + VC_RECT_T *src_rect, VC_RECT_T *dst_rect, + VC_IMAGE_TRANSFORM_T *flipmask) { - struct weston_output *output_base = surface->surface->output; + struct weston_output *output_base = view->view->surface->output; struct rpir_output *output = to_rpir_output(output_base); - struct weston_matrix matrix = surface->surface->transform.matrix; + struct weston_matrix matrix = view->view->transform.matrix; VC_IMAGE_TRANSFORM_T flipt = 0; int src_x, src_y; int dst_x, dst_y; @@ -523,14 +697,15 @@ rpir_surface_compute_rects(struct rpir_surface *surface, src_x = 0 << 16; src_y = 0 << 16; - if (surface->buffer_type == BUFFER_TYPE_EGL) { - struct weston_buffer *buffer = surface->egl_front->buffer_ref.buffer; + if (view->surface->buffer_type == BUFFER_TYPE_EGL) { + struct weston_buffer *buffer = + view->surface->egl_front->buffer_ref.buffer; src_width = buffer->width << 16; src_height = buffer->height << 16; } else { - src_width = surface->front->width << 16; - src_height = surface->front->height << 16; + src_width = view->surface->front->width << 16; + src_height = view->surface->front->height << 16; } weston_matrix_multiply(&matrix, &output->matrix); @@ -541,7 +716,7 @@ rpir_surface_compute_rects(struct rpir_surface *surface, if (matrix.type >= WESTON_MATRIX_TRANSFORM_ROTATE) { #endif warn_bad_matrix(&matrix, &output->matrix, - &surface->surface->transform.matrix); + &view->view->transform.matrix); } else { if (matrix.type & WESTON_MATRIX_TRANSFORM_ROTATE) { if (fabsf(matrix.d[0]) < 1e-4f && @@ -552,13 +727,13 @@ rpir_surface_compute_rects(struct rpir_surface *surface, /* no transpose */ } else { warn_bad_matrix(&matrix, &output->matrix, - &surface->surface->transform.matrix); + &view->view->transform.matrix); } } } - p2.f[0] = surface->surface->geometry.width; - p2.f[1] = surface->surface->geometry.height; + p2.f[0] = view->view->surface->width; + p2.f[1] = view->view->surface->height; /* transform top-left and bot-right corner into screen coordinates */ weston_matrix_transform(&matrix, &p1); @@ -680,9 +855,9 @@ rpir_surface_compute_rects(struct rpir_surface *surface, src_width = int_max(src_width, 0); src_height = int_max(src_height, 0); - DBG("rpir_surface %p %dx%d: p1 %f, %f; p2 %f, %f\n", surface, - surface->surface->geometry.width, - surface->surface->geometry.height, + DBG("rpir_view %p %dx%d: p1 %f, %f; p2 %f, %f\n", view, + view->view->geometry.width, + view->view->geometry.height, p1.f[0], p1.f[1], p2.f[0], p2.f[1]); DBG("src rect %d;%d, %d;%d, %d;%dx%d;%d\n", src_x >> 16, src_x & 0xffff, @@ -706,7 +881,7 @@ rpir_surface_compute_rects(struct rpir_surface *surface, } /* EGL buffers will be upside-down related to what DispmanX expects */ - if (surface->buffer_type == BUFFER_TYPE_EGL) + if (view->surface->buffer_type == BUFFER_TYPE_EGL) flipt ^= TRANSFORM_VFLIP; vc_dispmanx_rect_set(src_rect, src_x, src_y, src_width, src_height); @@ -743,7 +918,6 @@ vc_image2dispmanx_transform(VC_IMAGE_TRANSFORM_T t) } } - static DISPMANX_RESOURCE_HANDLE_T rpir_surface_get_resource(struct rpir_surface *surface) { @@ -759,9 +933,40 @@ rpir_surface_get_resource(struct rpir_surface *surface) } } +#ifdef HAVE_ELEMENT_SET_OPAQUE_RECT static int -rpir_surface_dmx_add(struct rpir_surface *surface, struct rpir_output *output, - DISPMANX_UPDATE_HANDLE_T update, int layer) +rpir_surface_set_opaque_rect(struct rpir_surface *surface, + DISPMANX_UPDATE_HANDLE_T update) +{ + int ret; + + if (pixman_region32_not_empty(&surface->surface->opaque) && + surface->opaque_regions) { + pixman_box32_t *box; + VC_RECT_T opaque_rect; + + box = pixman_region32_extents(&surface->surface->opaque); + opaque_rect.x = box->x1; + opaque_rect.y = box->y1; + opaque_rect.width = box->x2 - box->x1; + opaque_rect.height = box->y2 - box->y1; + + ret = vc_dispmanx_element_set_opaque_rect(update, + surface->handle, + &opaque_rect); + if (ret) { + weston_log("vc_dispmanx_element_set_opaque_rect failed\n"); + return -1; + } + } + + return 0; +} +#endif + +static int +rpir_view_dmx_add(struct rpir_view *view, struct rpir_output *output, + DISPMANX_UPDATE_HANDLE_T update, int layer) { /* Do not use DISPMANX_FLAGS_ALPHA_PREMULT here. * If you define PREMULT and ALPHA_MIX, the hardware will not @@ -771,7 +976,7 @@ rpir_surface_dmx_add(struct rpir_surface *surface, struct rpir_output *output, VC_DISPMANX_ALPHA_T alphasetup = { DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_MIX, - float2uint8(surface->surface->alpha), /* opacity 0-255 */ + float2uint8(view->view->alpha), /* opacity 0-255 */ 0 /* mask resource handle */ }; VC_RECT_T dst_rect; @@ -780,18 +985,17 @@ rpir_surface_dmx_add(struct rpir_surface *surface, struct rpir_output *output, int ret; DISPMANX_RESOURCE_HANDLE_T resource_handle; - resource_handle = rpir_surface_get_resource(surface); + resource_handle = rpir_surface_get_resource(view->surface); if (resource_handle == DISPMANX_NO_HANDLE) { weston_log("%s: no buffer yet, aborting\n", __func__); return 0; } - ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect, - &flipmask); + ret = rpir_view_compute_rects(view, &src_rect, &dst_rect, &flipmask); if (ret < 0) return 0; - surface->handle = vc_dispmanx_element_add( + view->handle = vc_dispmanx_element_add( update, output->display, layer, @@ -802,37 +1006,48 @@ rpir_surface_dmx_add(struct rpir_surface *surface, struct rpir_output *output, &alphasetup, NULL /* clamp */, vc_image2dispmanx_transform(flipmask)); - DBG("rpir_surface %p add %u, alpha %f resource %d\n", surface, - surface->handle, surface->surface->alpha, resource_handle); + DBG("rpir_surface %p add %u, alpha %f resource %d\n", view, + view->handle, view->view->alpha, resource_handle); + + if (view->handle == DISPMANX_NO_HANDLE) + return -1; + +#ifdef HAVE_ELEMENT_SET_OPAQUE_RECT + ret = rpir_surface_set_opaque_rect(surface, update); + if (ret < 0) + return -1; +#endif + + view->surface->visible_views++; return 1; } static void -rpir_surface_dmx_swap(struct rpir_surface *surface, - DISPMANX_UPDATE_HANDLE_T update) +rpir_view_dmx_swap(struct rpir_view *view, + DISPMANX_UPDATE_HANDLE_T update) { VC_RECT_T rect; pixman_box32_t *r; /* XXX: skip, iff resource was not reallocated, and single-buffering */ - vc_dispmanx_element_change_source(update, surface->handle, - surface->front->handle); + vc_dispmanx_element_change_source(update, view->handle, + view->surface->front->handle); /* This is current damage now, after rpir_surface_damage() */ - r = pixman_region32_extents(&surface->prev_damage); + r = pixman_region32_extents(&view->surface->prev_damage); vc_dispmanx_rect_set(&rect, r->x1, r->y1, r->x2 - r->x1, r->y2 - r->y1); - vc_dispmanx_element_modified(update, surface->handle, &rect); - DBG("rpir_surface %p swap\n", surface); + vc_dispmanx_element_modified(update, view->handle, &rect); + DBG("rpir_view %p swap\n", view); } static int -rpir_surface_dmx_move(struct rpir_surface *surface, - DISPMANX_UPDATE_HANDLE_T update, int layer) +rpir_view_dmx_move(struct rpir_view *view, + DISPMANX_UPDATE_HANDLE_T update, int layer) { - uint8_t alpha = float2uint8(surface->surface->alpha); + uint8_t alpha = float2uint8(view->view->alpha); VC_RECT_T dst_rect; VC_RECT_T src_rect; VC_IMAGE_TRANSFORM_T flipmask; @@ -840,28 +1055,27 @@ rpir_surface_dmx_move(struct rpir_surface *surface, /* XXX: return early, if all attributes stay the same */ - if (surface->buffer_type == BUFFER_TYPE_EGL) { + if (view->surface->buffer_type == BUFFER_TYPE_EGL) { DISPMANX_RESOURCE_HANDLE_T resource_handle; - resource_handle = rpir_surface_get_resource(surface); + resource_handle = rpir_surface_get_resource(view->surface); if (resource_handle == DISPMANX_NO_HANDLE) { weston_log("%s: no buffer yet, aborting\n", __func__); return 0; } vc_dispmanx_element_change_source(update, - surface->handle, + view->handle, resource_handle); } - ret = rpir_surface_compute_rects(surface, &src_rect, &dst_rect, - &flipmask); + ret = rpir_view_compute_rects(view, &src_rect, &dst_rect, &flipmask); if (ret < 0) return 0; ret = vc_dispmanx_element_change_attributes( update, - surface->handle, + view->handle, ELEMENT_CHANGE_LAYER | ELEMENT_CHANGE_OPACITY | ELEMENT_CHANGE_TRANSFORM | @@ -875,24 +1089,31 @@ rpir_surface_dmx_move(struct rpir_surface *surface, /* This really is DISPMANX_TRANSFORM_T, no matter * what the header says. */ vc_image2dispmanx_transform(flipmask)); - DBG("rpir_surface %p move\n", surface); + DBG("rpir_view %p move\n", view); if (ret) return -1; +#ifdef HAVE_ELEMENT_SET_OPAQUE_RECT + ret = rpir_surface_set_opaque_rect(surface, update); + if (ret < 0) + return -1; +#endif + return 1; } static void -rpir_surface_dmx_remove(struct rpir_surface *surface, - DISPMANX_UPDATE_HANDLE_T update) +rpir_view_dmx_remove(struct rpir_view *view, + DISPMANX_UPDATE_HANDLE_T update) { - if (surface->handle == DISPMANX_NO_HANDLE) + if (view->handle == DISPMANX_NO_HANDLE) return; - vc_dispmanx_element_remove(update, surface->handle); - DBG("rpir_surface %p remove %u\n", surface, surface->handle); - surface->handle = DISPMANX_NO_HANDLE; + vc_dispmanx_element_remove(update, view->handle); + DBG("rpir_view %p remove %u\n", view, view->handle); + view->handle = DISPMANX_NO_HANDLE; + view->surface->visible_views--; } static void @@ -900,23 +1121,32 @@ rpir_surface_swap_pointers(struct rpir_surface *surface) { struct rpi_resource *tmp; - tmp = surface->front; - surface->front = surface->back; - surface->back = tmp; - surface->need_swap = 0; - DBG("new back %p, new front %p\n", surface->back, surface->front); + if (surface->buffer_type == BUFFER_TYPE_EGL) { + if (surface->egl_back != NULL) { + assert(surface->egl_old_front == NULL); + surface->egl_old_front = surface->egl_front; + surface->egl_front = surface->egl_back; + surface->egl_back = NULL; + DBG("new front %d\n", surface->egl_front->resource_handle); + } + } else { + tmp = surface->front; + surface->front = surface->back; + surface->back = tmp; + DBG("new back %p, new front %p\n", surface->back, surface->front); + } } static int -is_surface_not_visible(struct weston_surface *surface) +is_view_not_visible(struct weston_view *view) { /* Return true, if surface is guaranteed to be totally obscured. */ int ret; pixman_region32_t unocc; pixman_region32_init(&unocc); - pixman_region32_subtract(&unocc, &surface->transform.boundingbox, - &surface->clip); + pixman_region32_subtract(&unocc, &view->transform.boundingbox, + &view->clip); ret = !pixman_region32_not_empty(&unocc); pixman_region32_fini(&unocc); @@ -924,81 +1154,54 @@ is_surface_not_visible(struct weston_surface *surface) } static void -rpir_surface_update(struct rpir_surface *surface, struct rpir_output *output, - DISPMANX_UPDATE_HANDLE_T update, int layer) +rpir_view_update(struct rpir_view *view, struct rpir_output *output, + DISPMANX_UPDATE_HANDLE_T update, int layer) { - int need_swap = surface->need_swap; int ret; int obscured; - if (surface->buffer_type == BUFFER_TYPE_EGL) { - if (surface->egl_back != NULL) { - assert(surface->egl_old_front == NULL); - surface->egl_old_front = surface->egl_front; - surface->egl_front = surface->egl_back; - surface->egl_back = NULL; - } - if (surface->egl_front->buffer_ref.buffer == NULL) { - weston_log("warning: client unreffed current front buffer\n"); - - wl_list_remove(&surface->link); - if (surface->handle == DISPMANX_NO_HANDLE) { - wl_list_init(&surface->link); - } else { - rpir_surface_dmx_remove(surface, update); - wl_list_insert(&output->surface_cleanup_list, - &surface->link); - } - - goto out; - } - } else { - if (need_swap) - rpir_surface_swap_pointers(surface); - } - - obscured = is_surface_not_visible(surface->surface); + obscured = is_view_not_visible(view->view); if (obscured) { - DBG("rpir_surface %p totally obscured.\n", surface); + DBG("rpir_view %p totally obscured.\n", view); - wl_list_remove(&surface->link); - if (surface->handle == DISPMANX_NO_HANDLE) { - wl_list_init(&surface->link); + wl_list_remove(&view->link); + if (view->handle == DISPMANX_NO_HANDLE) { + wl_list_init(&view->link); } else { - rpir_surface_dmx_remove(surface, update); - wl_list_insert(&output->surface_cleanup_list, - &surface->link); + rpir_view_dmx_remove(view, update); + wl_list_insert(&output->view_cleanup_list, + &view->link); } goto out; } - if (surface->handle == DISPMANX_NO_HANDLE) { - ret = rpir_surface_dmx_add(surface, output, update, layer); + if (view->handle == DISPMANX_NO_HANDLE) { + ret = rpir_view_dmx_add(view, output, update, layer); if (ret == 0) { - wl_list_remove(&surface->link); - wl_list_init(&surface->link); + wl_list_remove(&view->link); + wl_list_init(&view->link); } else if (ret < 0) { - weston_log("ERROR rpir_surface_dmx_add() failed.\n"); + weston_log("ERROR rpir_view_dmx_add() failed.\n"); } } else { - if (need_swap) - rpir_surface_dmx_swap(surface, update); + if (view->surface->need_swap) + rpir_view_dmx_swap(view, update); - ret = rpir_surface_dmx_move(surface, update, layer); + ret = rpir_view_dmx_move(view, update, layer); if (ret == 0) { - rpir_surface_dmx_remove(surface, update); + rpir_view_dmx_remove(view, update); - wl_list_remove(&surface->link); - wl_list_insert(&output->surface_cleanup_list, - &surface->link); + wl_list_remove(&view->link); + wl_list_insert(&output->view_cleanup_list, + &view->link); } else if (ret < 0) { - weston_log("ERROR rpir_surface_dmx_move() failed.\n"); + weston_log("ERROR rpir_view_dmx_move() failed.\n"); } } out: - surface->layer = layer; + view->layer = layer; } static int @@ -1105,15 +1308,15 @@ static void rpir_output_dmx_remove_all(struct rpir_output *output, DISPMANX_UPDATE_HANDLE_T update) { - struct rpir_surface *surface; + struct rpir_view *view; - while (!wl_list_empty(&output->surface_list)) { - surface = container_of(output->surface_list.next, - struct rpir_surface, link); - rpir_surface_dmx_remove(surface, update); + while (!wl_list_empty(&output->view_list)) { + view = container_of(output->view_list.next, + struct rpir_view, link); + rpir_view_dmx_remove(view, update); - wl_list_remove(&surface->link); - wl_list_insert(&output->surface_cleanup_list, &surface->link); + wl_list_remove(&view->link); + wl_list_insert(&output->view_cleanup_list, &view->link); } } @@ -1186,8 +1389,8 @@ rpi_renderer_repaint_output(struct weston_output *base, { struct weston_compositor *compositor = base->compositor; struct rpir_output *output = to_rpir_output(base); - struct weston_surface *ws; - struct rpir_surface *surface; + struct weston_view *wv; + struct rpir_view *view; struct wl_list done_list; int layer = 1; @@ -1199,27 +1402,66 @@ rpi_renderer_repaint_output(struct weston_output *base, free(output->capture_data); output->capture_data = NULL; + /* Swap resources on surfaces as needed */ + wl_list_for_each_reverse(wv, &compositor->view_list, link) + wv->surface->touched = 0; + + wl_list_for_each_reverse(wv, &compositor->view_list, link) { + view = to_rpir_view(wv); + + if (!wv->surface->touched) { + wv->surface->touched = 1; + + if (view->surface->buffer_type == BUFFER_TYPE_EGL || + view->surface->need_swap) + rpir_surface_swap_pointers(view->surface); + } + + if (view->surface->buffer_type == BUFFER_TYPE_EGL) { + struct weston_buffer *buffer; + buffer = view->surface->egl_front->buffer_ref.buffer; + if (buffer != NULL) { + rpi_buffer_egl_lock(buffer); + } else { + weston_log("warning: client destroyed current front buffer\n"); + + wl_list_remove(&view->link); + if (view->handle == DISPMANX_NO_HANDLE) { + wl_list_init(&view->link); + } else { + rpir_view_dmx_remove(view, output->update); + wl_list_insert(&output->view_cleanup_list, + &view->link); + } + } + } + } + /* update all renderable surfaces */ wl_list_init(&done_list); - wl_list_for_each_reverse(ws, &compositor->surface_list, link) { - if (ws->plane != &compositor->primary_plane) + wl_list_for_each_reverse(wv, &compositor->view_list, link) { + if (wv->plane != &compositor->primary_plane) continue; - surface = to_rpir_surface(ws); - assert(!wl_list_empty(&surface->link) || - surface->handle == DISPMANX_NO_HANDLE); + view = to_rpir_view(wv); + assert(!wl_list_empty(&view->link) || + view->handle == DISPMANX_NO_HANDLE); - wl_list_remove(&surface->link); - wl_list_insert(&done_list, &surface->link); - rpir_surface_update(surface, output, output->update, layer++); + wl_list_remove(&view->link); + wl_list_insert(&done_list, &view->link); + rpir_view_update(view, output, output->update, layer++); } + /* Mark all surfaces as swapped */ + wl_list_for_each_reverse(wv, &compositor->view_list, link) + to_rpir_surface(wv->surface)->need_swap = 0; + /* Remove all surfaces that are still on screen, but were * not rendered this time. */ rpir_output_dmx_remove_all(output, output->update); - wl_list_insert_list(&output->surface_list, &done_list); + wl_list_insert_list(&output->view_list, &done_list); output->update = DISPMANX_NO_HANDLE; /* The frame_signal is emitted in rpi_renderer_finish_frame(), @@ -1263,7 +1505,7 @@ rpi_renderer_attach(struct weston_surface *base, struct weston_buffer *buffer) /* XXX: need to check if in middle of update */ rpi_resource_release(surface->back); - if (surface->handle == DISPMANX_NO_HANDLE) + if (!surface->visible_views) /* XXX: cannot do this, if middle of an update */ rpi_resource_release(surface->front); @@ -1319,6 +1561,27 @@ rpi_renderer_attach(struct weston_surface *base, struct weston_buffer *buffer) } } +static void +rpir_surface_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct rpir_surface *surface; + struct weston_surface *base = data; + + surface = container_of(listener, struct rpir_surface, + surface_destroy_listener); + + assert(surface); + assert(surface->surface == base); + if (!surface) + return; + + surface->surface = NULL; + base->renderer_state = NULL; + + if (wl_list_empty(&surface->views)) + rpir_surface_destroy(surface); +} + static int rpi_renderer_create_surface(struct weston_surface *base) { @@ -1333,6 +1596,35 @@ rpi_renderer_create_surface(struct weston_surface *base) surface->surface = base; base->renderer_state = surface; + + surface->surface_destroy_listener.notify = + rpir_surface_handle_surface_destroy; + wl_signal_add(&base->destroy_signal, + &surface->surface_destroy_listener); + + return 0; +} + +static int +rpi_renderer_create_view(struct weston_view *base) +{ + struct rpir_surface *surface = to_rpir_surface(base->surface); + struct rpir_view *view; + + assert(base->renderer_state == NULL); + + view = rpir_view_create(surface); + if (!view) + return -1; + + view->view = base; + base->renderer_state = view; + + view->view_destroy_listener.notify = + rpir_view_handle_view_destroy; + wl_signal_add(&base->destroy_signal, + &view->view_destroy_listener); + return 0; } @@ -1378,25 +1670,28 @@ rpi_renderer_surface_set_color(struct weston_surface *base, } static void -rpi_renderer_destroy_surface(struct weston_surface *base) +rpir_view_handle_view_destroy(struct wl_listener *listener, void *data) { - struct rpir_surface *surface = to_rpir_surface(base); + struct rpir_view *view; + struct weston_view *base = data; - assert(surface); - assert(surface->surface == base); - if (!surface) + view = container_of(listener, struct rpir_view, view_destroy_listener); + + assert(view); + assert(view->view == base); + if (!view) return; - surface->surface = NULL; + view->view = NULL; base->renderer_state = NULL; - /* If guaranteed to not be on screen, just detroy it. */ - if (wl_list_empty(&surface->link)) - rpir_surface_destroy(surface); + /* If guaranteed to not be on screen, just destroy it. */ + if (wl_list_empty(&view->link)) + rpir_view_destroy(view); - /* Otherwise, the surface is either on screen and needs + /* Otherwise, the view is either on screen and needs * to be removed by a repaint update, or it is in the - * surface_cleanup_list, and will be destroyed by + * view_cleanup_list, and will be destroyed by * rpi_renderer_finish_frame(). */ } @@ -1434,14 +1729,13 @@ rpi_renderer_create(struct weston_compositor *compositor, return -1; renderer->single_buffer = params->single_buffer; + renderer->enable_opaque_regions = params->opaque_regions; renderer->base.read_pixels = rpi_renderer_read_pixels; renderer->base.repaint_output = rpi_renderer_repaint_output; renderer->base.flush_damage = rpi_renderer_flush_damage; renderer->base.attach = rpi_renderer_attach; - renderer->base.create_surface = rpi_renderer_create_surface; renderer->base.surface_set_color = rpi_renderer_surface_set_color; - renderer->base.destroy_surface = rpi_renderer_destroy_surface; renderer->base.destroy = rpi_renderer_destroy; #ifdef ENABLE_EGL @@ -1504,8 +1798,8 @@ rpi_renderer_output_create(struct weston_output *base, output->display = display; output->update = DISPMANX_NO_HANDLE; - wl_list_init(&output->surface_list); - wl_list_init(&output->surface_cleanup_list); + wl_list_init(&output->view_list); + wl_list_init(&output->view_cleanup_list); rpi_resource_init(&output->capture_buffer); base->renderer_state = output; @@ -1516,7 +1810,7 @@ WL_EXPORT void rpi_renderer_output_destroy(struct weston_output *base) { struct rpir_output *output = to_rpir_output(base); - struct rpir_surface *surface; + struct rpir_view *view; DISPMANX_UPDATE_HANDLE_T update; rpi_resource_release(&output->capture_buffer); @@ -1527,12 +1821,10 @@ rpi_renderer_output_destroy(struct weston_output *base) rpir_output_dmx_remove_all(output, update); vc_dispmanx_update_submit_sync(update); - while (!wl_list_empty(&output->surface_cleanup_list)) { - surface = container_of(output->surface_cleanup_list.next, - struct rpir_surface, link); - if (surface->surface) - surface->surface->renderer_state = NULL; - rpir_surface_destroy(surface); + while (!wl_list_empty(&output->view_cleanup_list)) { + view = container_of(output->view_cleanup_list.next, + struct rpir_view, link); + rpir_view_destroy(view); } free(output); @@ -1553,41 +1845,37 @@ rpi_renderer_finish_frame(struct weston_output *base) { struct rpir_output *output = to_rpir_output(base); struct weston_compositor *compositor = base->compositor; - struct weston_surface *ws; - struct rpir_surface *surface; + struct weston_view *wv; + struct rpir_view *view; - while (!wl_list_empty(&output->surface_cleanup_list)) { - surface = container_of(output->surface_cleanup_list.next, - struct rpir_surface, link); + while (!wl_list_empty(&output->view_cleanup_list)) { + view = container_of(output->view_cleanup_list.next, + struct rpir_view, link); - if (surface->surface) { - /* The weston_surface still exists, but is + if (view->view) { + /* The weston_view still exists, but is * temporarily not visible, and hence its Element * was removed. The current front buffer contents * must be preserved. */ - if (!surface->single_buffer) - rpi_resource_release(surface->back); + if (!view->surface->visible_views) + rpi_resource_release(view->surface->back); - wl_list_remove(&surface->link); - wl_list_init(&surface->link); + wl_list_remove(&view->link); + wl_list_init(&view->link); } else { - rpir_surface_destroy(surface); + rpir_view_destroy(view); } } - wl_list_for_each(ws, &compositor->surface_list, link) { - surface = to_rpir_surface(ws); - - if (surface->buffer_type != BUFFER_TYPE_EGL) - continue; + wl_list_for_each(wv, &compositor->view_list, link) { + view = to_rpir_view(wv); - if(surface->egl_old_front == NULL) + if (view->surface->buffer_type != BUFFER_TYPE_EGL) continue; - weston_buffer_reference(&surface->egl_old_front->buffer_ref, NULL); - free(surface->egl_old_front); - surface->egl_old_front = NULL; + rpir_egl_buffer_destroy(view->surface->egl_old_front); + view->surface->egl_old_front = NULL; } wl_signal_emit(&base->frame_signal, base); diff --git a/src/rpi-renderer.h b/src/rpi-renderer.h index 28ae3039..885631ab 100644 --- a/src/rpi-renderer.h +++ b/src/rpi-renderer.h @@ -25,6 +25,7 @@ struct rpi_renderer_parameters { int single_buffer; + int opaque_regions; }; int diff --git a/src/screenshooter.c b/src/screenshooter.c index 645114d0..26e503ce 100644 --- a/src/screenshooter.c +++ b/src/screenshooter.c @@ -144,6 +144,8 @@ screenshooter_frame_notify(struct wl_listener *listener, void *data) d = wl_shm_buffer_get_data(l->buffer->shm_buffer); s = pixels + stride * (l->buffer->height - 1); + wl_shm_buffer_begin_access(l->buffer->shm_buffer); + switch (compositor->read_format) { case PIXMAN_a8r8g8b8: case PIXMAN_x8r8g8b8: @@ -163,6 +165,8 @@ screenshooter_frame_notify(struct wl_listener *listener, void *data) break; } + wl_shm_buffer_end_access(l->buffer->shm_buffer); + screenshooter_send_done(l->resource); free(pixels); free(l); @@ -263,7 +267,7 @@ struct weston_recorder { uint32_t total; int fd; struct wl_listener frame_listener; - int count; + int count, destroying; }; static uint32_t * @@ -298,58 +302,7 @@ component_delta(uint32_t next, uint32_t prev) } static void -transform_rect(struct weston_output *output, pixman_box32_t *r) -{ - pixman_box32_t s = *r; - - switch (output->transform) { - case WL_OUTPUT_TRANSFORM_FLIPPED: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - s.x1 = output->width - r->x2; - s.x2 = output->width - r->x1; - break; - default: - break; - } - - switch (output->transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - case WL_OUTPUT_TRANSFORM_FLIPPED: - r->x1 = s.x1; - r->x2 = s.x2; - break; - case WL_OUTPUT_TRANSFORM_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - r->x1 = output->current_mode->width - s.y2; - r->y1 = s.x1; - r->x2 = output->current_mode->width - s.y1; - r->y2 = s.x2; - break; - case WL_OUTPUT_TRANSFORM_180: - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - r->x1 = output->current_mode->width - s.x2; - r->y1 = output->current_mode->height - s.y2; - r->x2 = output->current_mode->width - s.x1; - r->y2 = output->current_mode->height - s.y1; - break; - case WL_OUTPUT_TRANSFORM_270: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - r->x1 = s.y1; - r->y1 = output->current_mode->height - s.x2; - r->x2 = s.y2; - r->y2 = output->current_mode->height - s.x1; - break; - default: - break; - } - - r->x1 *= output->current_scale; - r->y1 *= output->current_scale; - r->x2 *= output->current_scale; - r->y2 *= output->current_scale; -} +weston_recorder_destroy(struct weston_recorder *recorder); static void weston_recorder_frame_notify(struct wl_listener *listener, void *data) @@ -360,7 +313,7 @@ weston_recorder_frame_notify(struct wl_listener *listener, void *data) struct weston_compositor *compositor = output->compositor; uint32_t msecs = output->frame_time; pixman_box32_t *r; - pixman_region32_t damage; + pixman_region32_t damage, transformed_damage; int i, j, k, n, width, height, run, stride; uint32_t delta, prev, *d, *s, *p, next; struct { @@ -379,15 +332,20 @@ weston_recorder_frame_notify(struct wl_listener *listener, void *data) outbuf = recorder->tmpbuf; pixman_region32_init(&damage); + pixman_region32_init(&transformed_damage); pixman_region32_intersect(&damage, &output->region, &output->previous_damage); + pixman_region32_translate(&damage, -output->x, -output->y); + weston_transformed_region(output->width, output->height, + output->transform, output->current_scale, + &damage, &transformed_damage); + pixman_region32_fini(&damage); - r = pixman_region32_rectangles(&damage, &n); - if (n == 0) + r = pixman_region32_rectangles(&transformed_damage, &n); + if (n == 0) { + pixman_region32_fini(&transformed_damage); return; - - for (i = 0; i < n; i++) - transform_rect(output, &r[i]); + } header.msecs = msecs; header.nrects = n; @@ -450,8 +408,11 @@ weston_recorder_frame_notify(struct wl_listener *listener, void *data) #endif } - pixman_region32_fini(&damage); + pixman_region32_fini(&transformed_damage); recorder->count++; + + if (recorder->destroying) + weston_recorder_destroy(recorder); } static void @@ -473,6 +434,7 @@ weston_recorder_create(struct weston_output *output, const char *filename) recorder->rect = malloc(size); recorder->total = 0; recorder->count = 0; + recorder->destroying = 0; recorder->output = output; if (do_yflip) @@ -532,15 +494,18 @@ recorder_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *da { struct weston_seat *ws = (struct weston_seat *) seat; struct weston_compositor *ec = ws->compositor; - struct weston_output *output = - container_of(ec->output_list.next, - struct weston_output, link); - struct wl_listener *listener; + struct weston_output *output; + struct wl_listener *listener = NULL; struct weston_recorder *recorder; static const char filename[] = "capture.wcap"; - listener = wl_signal_get(&output->frame_signal, - weston_recorder_frame_notify); + wl_list_for_each(output, &seat->compositor->output_list, link) { + listener = wl_signal_get(&output->frame_signal, + weston_recorder_frame_notify); + if (listener) + break; + } + if (listener) { recorder = container_of(listener, struct weston_recorder, frame_listener); @@ -549,9 +514,18 @@ recorder_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *da "stopping recorder, total file size %dM, %d frames\n", recorder->total / (1024 * 1024), recorder->count); - weston_recorder_destroy(recorder); + recorder->destroying = 1; + weston_output_schedule_repaint(recorder->output); } else { - weston_log("starting recorder, file %s\n", filename); + if (seat->keyboard && seat->keyboard->focus && + seat->keyboard->focus->output) + output = seat->keyboard->focus->output; + else + output = container_of(ec->output_list.next, + struct weston_output, link); + + weston_log("starting recorder for output %s, file %s\n", + output->name, filename); weston_recorder_create(output, filename); } } diff --git a/src/shell.c b/src/shell.c deleted file mode 100644 index fa9ff544..00000000 --- a/src/shell.c +++ /dev/null @@ -1,4799 +0,0 @@ -/* - * Copyright © 2010-2012 Intel Corporation - * Copyright © 2011-2012 Collabora, Ltd. - * - * Permission to use, copy, modify, distribute, and sell this software and - * its documentation for any purpose is hereby granted without fee, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of the copyright holders not be used in - * advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. The copyright holders make - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "compositor.h" -#include "desktop-shell-server-protocol.h" -#include "input-method-server-protocol.h" -#include "workspaces-server-protocol.h" -#include "../shared/config-parser.h" - -#define DEFAULT_NUM_WORKSPACES 1 -#define DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH 200 - -enum animation_type { - ANIMATION_NONE, - - ANIMATION_ZOOM, - ANIMATION_FADE -}; - -enum fade_type { - FADE_IN, - FADE_OUT -}; - -struct focus_state { - struct weston_seat *seat; - struct workspace *ws; - struct weston_surface *keyboard_focus; - struct wl_list link; - struct wl_listener seat_destroy_listener; - struct wl_listener surface_destroy_listener; -}; - -struct workspace { - struct weston_layer layer; - - struct wl_list focus_list; - struct wl_listener seat_destroyed_listener; -}; - -struct input_panel_surface { - struct wl_resource *resource; - struct wl_signal destroy_signal; - - struct desktop_shell *shell; - - struct wl_list link; - struct weston_surface *surface; - struct wl_listener surface_destroy_listener; - - struct weston_output *output; - uint32_t panel; -}; - -struct desktop_shell { - struct weston_compositor *compositor; - - struct wl_listener idle_listener; - struct wl_listener wake_listener; - struct wl_listener destroy_listener; - struct wl_listener show_input_panel_listener; - struct wl_listener hide_input_panel_listener; - struct wl_listener update_input_panel_listener; - - struct weston_layer fullscreen_layer; - struct weston_layer panel_layer; - struct weston_layer background_layer; - struct weston_layer lock_layer; - struct weston_layer input_panel_layer; - - struct wl_listener pointer_focus_listener; - struct weston_surface *grab_surface; - - struct { - struct weston_process process; - struct wl_client *client; - struct wl_resource *desktop_shell; - - unsigned deathcount; - uint32_t deathstamp; - } child; - - bool locked; - bool showing_input_panels; - bool prepare_event_sent; - - struct { - struct weston_surface *surface; - pixman_box32_t cursor_rectangle; - } text_input; - - struct weston_surface *lock_surface; - struct wl_listener lock_surface_listener; - - struct { - struct wl_array array; - unsigned int current; - unsigned int num; - - struct wl_list client_list; - - struct weston_animation animation; - struct wl_list anim_sticky_list; - int anim_dir; - uint32_t anim_timestamp; - double anim_current; - struct workspace *anim_from; - struct workspace *anim_to; - } workspaces; - - struct { - char *path; - int duration; - struct wl_resource *binding; - struct weston_process process; - struct wl_event_source *timer; - } screensaver; - - struct { - struct wl_resource *binding; - struct wl_list surfaces; - } input_panel; - - struct { - struct weston_surface *surface; - struct weston_surface_animation *animation; - enum fade_type type; - struct wl_event_source *startup_timer; - } fade; - - uint32_t binding_modifier; - enum animation_type win_animation_type; - enum animation_type startup_animation_type; -}; - -enum shell_surface_type { - SHELL_SURFACE_NONE, - SHELL_SURFACE_TOPLEVEL, - SHELL_SURFACE_TRANSIENT, - SHELL_SURFACE_FULLSCREEN, - SHELL_SURFACE_MAXIMIZED, - SHELL_SURFACE_POPUP, - SHELL_SURFACE_XWAYLAND -}; - -struct ping_timer { - struct wl_event_source *source; - uint32_t serial; -}; - -struct shell_surface { - struct wl_resource *resource; - struct wl_signal destroy_signal; - - struct weston_surface *surface; - struct wl_listener surface_destroy_listener; - struct weston_surface *parent; - struct desktop_shell *shell; - - enum shell_surface_type type, next_type; - char *title, *class; - int32_t saved_x, saved_y; - bool saved_position_valid; - bool saved_rotation_valid; - int unresponsive; - - struct { - struct weston_transform transform; - struct weston_matrix rotation; - } rotation; - - struct { - struct wl_list grab_link; - int32_t x, y; - struct shell_seat *shseat; - uint32_t serial; - } popup; - - struct { - int32_t x, y; - uint32_t flags; - } transient; - - struct { - enum wl_shell_surface_fullscreen_method type; - struct weston_transform transform; /* matrix from x, y */ - uint32_t framerate; - struct weston_surface *black_surface; - } fullscreen; - - struct ping_timer *ping_timer; - - struct weston_transform workspace_transform; - - struct weston_output *fullscreen_output; - struct weston_output *output; - struct wl_list link; - - const struct weston_shell_client *client; -}; - -struct shell_grab { - struct weston_pointer_grab grab; - struct shell_surface *shsurf; - struct wl_listener shsurf_destroy_listener; -}; - -struct shell_touch_grab { - struct weston_touch_grab grab; - struct shell_surface *shsurf; - struct wl_listener shsurf_destroy_listener; - struct weston_touch *touch; -}; - -struct weston_move_grab { - struct shell_grab base; - wl_fixed_t dx, dy; -}; - -struct weston_touch_move_grab { - struct shell_touch_grab base; - wl_fixed_t dx, dy; -}; - -struct rotate_grab { - struct shell_grab base; - struct weston_matrix rotation; - struct { - float x; - float y; - } center; -}; - -struct shell_seat { - struct weston_seat *seat; - struct wl_listener seat_destroy_listener; - - struct { - struct weston_pointer_grab grab; - struct wl_list surfaces_list; - struct wl_client *client; - int32_t initial_up; - } popup_grab; -}; - -static void -activate(struct desktop_shell *shell, struct weston_surface *es, - struct weston_seat *seat); - -static struct workspace * -get_current_workspace(struct desktop_shell *shell); - -static struct shell_surface * -get_shell_surface(struct weston_surface *surface); - -static struct desktop_shell * -shell_surface_get_shell(struct shell_surface *shsurf); - -static void -surface_rotate(struct shell_surface *surface, struct weston_seat *seat); - -static void -shell_fade_startup(struct desktop_shell *shell); - -static bool -shell_surface_is_top_fullscreen(struct shell_surface *shsurf) -{ - struct desktop_shell *shell; - struct weston_surface *top_fs_es; - - shell = shell_surface_get_shell(shsurf); - - if (wl_list_empty(&shell->fullscreen_layer.surface_list)) - return false; - - top_fs_es = container_of(shell->fullscreen_layer.surface_list.next, - struct weston_surface, - layer_link); - return (shsurf == get_shell_surface(top_fs_es)); -} - -static void -destroy_shell_grab_shsurf(struct wl_listener *listener, void *data) -{ - struct shell_grab *grab; - - grab = container_of(listener, struct shell_grab, - shsurf_destroy_listener); - - grab->shsurf = NULL; -} - -static void -popup_grab_end(struct weston_pointer *pointer); - -static void -shell_grab_start(struct shell_grab *grab, - const struct weston_pointer_grab_interface *interface, - struct shell_surface *shsurf, - struct weston_pointer *pointer, - enum desktop_shell_cursor cursor) -{ - struct desktop_shell *shell = shsurf->shell; - - popup_grab_end(pointer); - - grab->grab.interface = interface; - grab->shsurf = shsurf; - grab->shsurf_destroy_listener.notify = destroy_shell_grab_shsurf; - wl_signal_add(&shsurf->destroy_signal, - &grab->shsurf_destroy_listener); - - weston_pointer_start_grab(pointer, &grab->grab); - if (shell->child.desktop_shell) { - desktop_shell_send_grab_cursor(shell->child.desktop_shell, - cursor); - weston_pointer_set_focus(pointer, shell->grab_surface, - wl_fixed_from_int(0), - wl_fixed_from_int(0)); - } -} - -static void -shell_grab_end(struct shell_grab *grab) -{ - if (grab->shsurf) - wl_list_remove(&grab->shsurf_destroy_listener.link); - - weston_pointer_end_grab(grab->grab.pointer); -} - -static void -shell_touch_grab_start(struct shell_touch_grab *grab, - const struct weston_touch_grab_interface *interface, - struct shell_surface *shsurf, - struct weston_touch *touch) -{ - struct desktop_shell *shell = shsurf->shell; - - grab->grab.interface = interface; - grab->shsurf = shsurf; - grab->shsurf_destroy_listener.notify = destroy_shell_grab_shsurf; - wl_signal_add(&shsurf->destroy_signal, - &grab->shsurf_destroy_listener); - - grab->touch = touch; - - weston_touch_start_grab(touch, &grab->grab); - if (shell->child.desktop_shell) - weston_touch_set_focus(touch->seat, shell->grab_surface); -} - -static void -shell_touch_grab_end(struct shell_touch_grab *grab) -{ - if (grab->shsurf) - wl_list_remove(&grab->shsurf_destroy_listener.link); - - weston_touch_end_grab(grab->touch); -} - -static void -center_on_output(struct weston_surface *surface, - struct weston_output *output); - -static enum weston_keyboard_modifier -get_modifier(char *modifier) -{ - if (!modifier) - return MODIFIER_SUPER; - - if (!strcmp("ctrl", modifier)) - return MODIFIER_CTRL; - else if (!strcmp("alt", modifier)) - return MODIFIER_ALT; - else if (!strcmp("super", modifier)) - return MODIFIER_SUPER; - else - return MODIFIER_SUPER; -} - -static enum animation_type -get_animation_type(char *animation) -{ - if (!strcmp("zoom", animation)) - return ANIMATION_ZOOM; - else if (!strcmp("fade", animation)) - return ANIMATION_FADE; - else - return ANIMATION_NONE; -} - -static void -shell_configuration(struct desktop_shell *shell) -{ - struct weston_config_section *section; - int duration; - char *s; - - section = weston_config_get_section(shell->compositor->config, - "screensaver", NULL, NULL); - weston_config_section_get_string(section, - "path", &shell->screensaver.path, NULL); - weston_config_section_get_int(section, "duration", &duration, 60); - shell->screensaver.duration = duration * 1000; - - section = weston_config_get_section(shell->compositor->config, - "shell", NULL, NULL); - weston_config_section_get_string(section, - "binding-modifier", &s, "super"); - shell->binding_modifier = get_modifier(s); - free(s); - weston_config_section_get_string(section, "animation", &s, "none"); - shell->win_animation_type = get_animation_type(s); - free(s); - weston_config_section_get_string(section, - "startup-animation", &s, "fade"); - shell->startup_animation_type = get_animation_type(s); - free(s); - if (shell->startup_animation_type == ANIMATION_ZOOM) - shell->startup_animation_type = ANIMATION_NONE; - - weston_config_section_get_uint(section, "num-workspaces", - &shell->workspaces.num, - DEFAULT_NUM_WORKSPACES); -} - -static void -focus_state_destroy(struct focus_state *state) -{ - wl_list_remove(&state->seat_destroy_listener.link); - wl_list_remove(&state->surface_destroy_listener.link); - free(state); -} - -static void -focus_state_seat_destroy(struct wl_listener *listener, void *data) -{ - struct focus_state *state = container_of(listener, - struct focus_state, - seat_destroy_listener); - - wl_list_remove(&state->link); - focus_state_destroy(state); -} - -static void -focus_state_surface_destroy(struct wl_listener *listener, void *data) -{ - struct focus_state *state = container_of(listener, - struct focus_state, - surface_destroy_listener); - struct desktop_shell *shell; - struct weston_surface *main_surface; - struct weston_surface *surface, *next; - - main_surface = weston_surface_get_main_surface(state->keyboard_focus); - - next = NULL; - wl_list_for_each(surface, &state->ws->layer.surface_list, layer_link) { - if (surface == main_surface) - continue; - - next = surface; - break; - } - - /* if the focus was a sub-surface, activate its main surface */ - if (main_surface != state->keyboard_focus) - next = main_surface; - - if (next) { - shell = state->seat->compositor->shell_interface.shell; - activate(shell, next, state->seat); - } else { - wl_list_remove(&state->link); - focus_state_destroy(state); - } -} - -static struct focus_state * -focus_state_create(struct weston_seat *seat, struct workspace *ws) -{ - struct focus_state *state; - - state = malloc(sizeof *state); - if (state == NULL) - return NULL; - - state->ws = ws; - state->seat = seat; - wl_list_insert(&ws->focus_list, &state->link); - - state->seat_destroy_listener.notify = focus_state_seat_destroy; - state->surface_destroy_listener.notify = focus_state_surface_destroy; - wl_signal_add(&seat->destroy_signal, - &state->seat_destroy_listener); - wl_list_init(&state->surface_destroy_listener.link); - - return state; -} - -static struct focus_state * -ensure_focus_state(struct desktop_shell *shell, struct weston_seat *seat) -{ - struct workspace *ws = get_current_workspace(shell); - struct focus_state *state; - - wl_list_for_each(state, &ws->focus_list, link) - if (state->seat == seat) - break; - - if (&state->link == &ws->focus_list) - state = focus_state_create(seat, ws); - - return state; -} - -static void -restore_focus_state(struct desktop_shell *shell, struct workspace *ws) -{ - struct focus_state *state, *next; - struct weston_surface *surface; - - wl_list_for_each_safe(state, next, &ws->focus_list, link) { - surface = state->keyboard_focus; - - weston_keyboard_set_focus(state->seat->keyboard, surface); - } -} - -static void -replace_focus_state(struct desktop_shell *shell, struct workspace *ws, - struct weston_seat *seat) -{ - struct focus_state *state; - struct weston_surface *surface; - - wl_list_for_each(state, &ws->focus_list, link) { - if (state->seat == seat) { - surface = seat->keyboard->focus; - state->keyboard_focus = surface; - return; - } - } -} - -static void -drop_focus_state(struct desktop_shell *shell, struct workspace *ws, - struct weston_surface *surface) -{ - struct focus_state *state; - - wl_list_for_each(state, &ws->focus_list, link) - if (state->keyboard_focus == surface) - state->keyboard_focus = NULL; -} - -static void -workspace_destroy(struct workspace *ws) -{ - struct focus_state *state, *next; - - wl_list_for_each_safe(state, next, &ws->focus_list, link) - focus_state_destroy(state); - - free(ws); -} - -static void -seat_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_seat *seat = data; - struct focus_state *state, *next; - struct workspace *ws = container_of(listener, - struct workspace, - seat_destroyed_listener); - - wl_list_for_each_safe(state, next, &ws->focus_list, link) - if (state->seat == seat) - wl_list_remove(&state->link); -} - -static struct workspace * -workspace_create(void) -{ - struct workspace *ws = malloc(sizeof *ws); - if (ws == NULL) - return NULL; - - weston_layer_init(&ws->layer, NULL); - - wl_list_init(&ws->focus_list); - wl_list_init(&ws->seat_destroyed_listener.link); - ws->seat_destroyed_listener.notify = seat_destroyed; - - return ws; -} - -static int -workspace_is_empty(struct workspace *ws) -{ - return wl_list_empty(&ws->layer.surface_list); -} - -static struct workspace * -get_workspace(struct desktop_shell *shell, unsigned int index) -{ - struct workspace **pws = shell->workspaces.array.data; - assert(index < shell->workspaces.num); - pws += index; - return *pws; -} - -static struct workspace * -get_current_workspace(struct desktop_shell *shell) -{ - return get_workspace(shell, shell->workspaces.current); -} - -static void -activate_workspace(struct desktop_shell *shell, unsigned int index) -{ - struct workspace *ws; - - ws = get_workspace(shell, index); - wl_list_insert(&shell->panel_layer.link, &ws->layer.link); - - shell->workspaces.current = index; -} - -static unsigned int -get_output_height(struct weston_output *output) -{ - return abs(output->region.extents.y1 - output->region.extents.y2); -} - -static void -surface_translate(struct weston_surface *surface, double d) -{ - struct shell_surface *shsurf = get_shell_surface(surface); - struct weston_transform *transform; - - transform = &shsurf->workspace_transform; - if (wl_list_empty(&transform->link)) - wl_list_insert(surface->geometry.transformation_list.prev, - &shsurf->workspace_transform.link); - - weston_matrix_init(&shsurf->workspace_transform.matrix); - weston_matrix_translate(&shsurf->workspace_transform.matrix, - 0.0, d, 0.0); - weston_surface_geometry_dirty(surface); -} - -static void -workspace_translate_out(struct workspace *ws, double fraction) -{ - struct weston_surface *surface; - unsigned int height; - double d; - - wl_list_for_each(surface, &ws->layer.surface_list, layer_link) { - height = get_output_height(surface->output); - d = height * fraction; - - surface_translate(surface, d); - } -} - -static void -workspace_translate_in(struct workspace *ws, double fraction) -{ - struct weston_surface *surface; - unsigned int height; - double d; - - wl_list_for_each(surface, &ws->layer.surface_list, layer_link) { - height = get_output_height(surface->output); - - if (fraction > 0) - d = -(height - height * fraction); - else - d = height + height * fraction; - - surface_translate(surface, d); - } -} - -static void -broadcast_current_workspace_state(struct desktop_shell *shell) -{ - struct wl_resource *resource; - - wl_resource_for_each(resource, &shell->workspaces.client_list) - workspace_manager_send_state(resource, - shell->workspaces.current, - shell->workspaces.num); -} - -static void -reverse_workspace_change_animation(struct desktop_shell *shell, - unsigned int index, - struct workspace *from, - struct workspace *to) -{ - shell->workspaces.current = index; - - shell->workspaces.anim_to = to; - shell->workspaces.anim_from = from; - shell->workspaces.anim_dir = -1 * shell->workspaces.anim_dir; - shell->workspaces.anim_timestamp = 0; - - weston_compositor_schedule_repaint(shell->compositor); -} - -static void -workspace_deactivate_transforms(struct workspace *ws) -{ - struct weston_surface *surface; - struct shell_surface *shsurf; - - wl_list_for_each(surface, &ws->layer.surface_list, layer_link) { - shsurf = get_shell_surface(surface); - if (!wl_list_empty(&shsurf->workspace_transform.link)) { - wl_list_remove(&shsurf->workspace_transform.link); - wl_list_init(&shsurf->workspace_transform.link); - } - weston_surface_geometry_dirty(surface); - } -} - -static void -finish_workspace_change_animation(struct desktop_shell *shell, - struct workspace *from, - struct workspace *to) -{ - weston_compositor_schedule_repaint(shell->compositor); - - wl_list_remove(&shell->workspaces.animation.link); - workspace_deactivate_transforms(from); - workspace_deactivate_transforms(to); - shell->workspaces.anim_to = NULL; - - wl_list_remove(&shell->workspaces.anim_from->layer.link); -} - -static void -animate_workspace_change_frame(struct weston_animation *animation, - struct weston_output *output, uint32_t msecs) -{ - struct desktop_shell *shell = - container_of(animation, struct desktop_shell, - workspaces.animation); - struct workspace *from = shell->workspaces.anim_from; - struct workspace *to = shell->workspaces.anim_to; - uint32_t t; - double x, y; - - if (workspace_is_empty(from) && workspace_is_empty(to)) { - finish_workspace_change_animation(shell, from, to); - return; - } - - if (shell->workspaces.anim_timestamp == 0) { - if (shell->workspaces.anim_current == 0.0) - shell->workspaces.anim_timestamp = msecs; - else - shell->workspaces.anim_timestamp = - msecs - - /* Invers of movement function 'y' below. */ - (asin(1.0 - shell->workspaces.anim_current) * - DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH * - M_2_PI); - } - - t = msecs - shell->workspaces.anim_timestamp; - - /* - * x = [0, π/2] - * y(x) = sin(x) - */ - x = t * (1.0/DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) * M_PI_2; - y = sin(x); - - if (t < DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) { - weston_compositor_schedule_repaint(shell->compositor); - - workspace_translate_out(from, shell->workspaces.anim_dir * y); - workspace_translate_in(to, shell->workspaces.anim_dir * y); - shell->workspaces.anim_current = y; - - weston_compositor_schedule_repaint(shell->compositor); - } - else - finish_workspace_change_animation(shell, from, to); -} - -static void -animate_workspace_change(struct desktop_shell *shell, - unsigned int index, - struct workspace *from, - struct workspace *to) -{ - struct weston_output *output; - - int dir; - - if (index > shell->workspaces.current) - dir = -1; - else - dir = 1; - - shell->workspaces.current = index; - - shell->workspaces.anim_dir = dir; - shell->workspaces.anim_from = from; - shell->workspaces.anim_to = to; - shell->workspaces.anim_current = 0.0; - shell->workspaces.anim_timestamp = 0; - - output = container_of(shell->compositor->output_list.next, - struct weston_output, link); - wl_list_insert(&output->animation_list, - &shell->workspaces.animation.link); - - wl_list_insert(from->layer.link.prev, &to->layer.link); - - workspace_translate_in(to, 0); - - restore_focus_state(shell, to); - - weston_compositor_schedule_repaint(shell->compositor); -} - -static void -update_workspace(struct desktop_shell *shell, unsigned int index, - struct workspace *from, struct workspace *to) -{ - shell->workspaces.current = index; - wl_list_insert(&from->layer.link, &to->layer.link); - wl_list_remove(&from->layer.link); -} - -static void -change_workspace(struct desktop_shell *shell, unsigned int index) -{ - struct workspace *from; - struct workspace *to; - - if (index == shell->workspaces.current) - return; - - /* Don't change workspace when there is any fullscreen surfaces. */ - if (!wl_list_empty(&shell->fullscreen_layer.surface_list)) - return; - - from = get_current_workspace(shell); - to = get_workspace(shell, index); - - if (shell->workspaces.anim_from == to && - shell->workspaces.anim_to == from) { - restore_focus_state(shell, to); - reverse_workspace_change_animation(shell, index, from, to); - broadcast_current_workspace_state(shell); - return; - } - - if (shell->workspaces.anim_to != NULL) - finish_workspace_change_animation(shell, - shell->workspaces.anim_from, - shell->workspaces.anim_to); - - restore_focus_state(shell, to); - - if (workspace_is_empty(to) && workspace_is_empty(from)) - update_workspace(shell, index, from, to); - else - animate_workspace_change(shell, index, from, to); - - broadcast_current_workspace_state(shell); -} - -static bool -workspace_has_only(struct workspace *ws, struct weston_surface *surface) -{ - struct wl_list *list = &ws->layer.surface_list; - struct wl_list *e; - - if (wl_list_empty(list)) - return false; - - e = list->next; - - if (e->next != list) - return false; - - return container_of(e, struct weston_surface, layer_link) == surface; -} - -static void -move_surface_to_workspace(struct desktop_shell *shell, - struct weston_surface *surface, - uint32_t workspace) -{ - struct workspace *from; - struct workspace *to; - struct weston_seat *seat; - struct weston_surface *focus; - - assert(weston_surface_get_main_surface(surface) == surface); - - if (workspace == shell->workspaces.current) - return; - - if (workspace >= shell->workspaces.num) - workspace = shell->workspaces.num - 1; - - from = get_current_workspace(shell); - to = get_workspace(shell, workspace); - - wl_list_remove(&surface->layer_link); - wl_list_insert(&to->layer.surface_list, &surface->layer_link); - - drop_focus_state(shell, from, surface); - wl_list_for_each(seat, &shell->compositor->seat_list, link) { - if (!seat->keyboard) - continue; - - focus = weston_surface_get_main_surface(seat->keyboard->focus); - if (focus == surface) - weston_keyboard_set_focus(seat->keyboard, NULL); - } - - weston_surface_damage_below(surface); -} - -static void -take_surface_to_workspace_by_seat(struct desktop_shell *shell, - struct weston_seat *seat, - unsigned int index) -{ - struct weston_surface *surface; - struct shell_surface *shsurf; - struct workspace *from; - struct workspace *to; - struct focus_state *state; - - surface = weston_surface_get_main_surface(seat->keyboard->focus); - if (surface == NULL || - index == shell->workspaces.current) - return; - - from = get_current_workspace(shell); - to = get_workspace(shell, index); - - wl_list_remove(&surface->layer_link); - wl_list_insert(&to->layer.surface_list, &surface->layer_link); - - replace_focus_state(shell, to, seat); - drop_focus_state(shell, from, surface); - - if (shell->workspaces.anim_from == to && - shell->workspaces.anim_to == from) { - wl_list_remove(&to->layer.link); - wl_list_insert(from->layer.link.prev, &to->layer.link); - - reverse_workspace_change_animation(shell, index, from, to); - broadcast_current_workspace_state(shell); - - return; - } - - if (shell->workspaces.anim_to != NULL) - finish_workspace_change_animation(shell, - shell->workspaces.anim_from, - shell->workspaces.anim_to); - - if (workspace_is_empty(from) && - workspace_has_only(to, surface)) - update_workspace(shell, index, from, to); - else { - shsurf = get_shell_surface(surface); - if (wl_list_empty(&shsurf->workspace_transform.link)) - wl_list_insert(&shell->workspaces.anim_sticky_list, - &shsurf->workspace_transform.link); - - animate_workspace_change(shell, index, from, to); - } - - broadcast_current_workspace_state(shell); - - state = ensure_focus_state(shell, seat); - if (state != NULL) - state->keyboard_focus = surface; -} - -static void -workspace_manager_move_surface(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *surface_resource, - uint32_t workspace) -{ - struct desktop_shell *shell = wl_resource_get_user_data(resource); - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - struct weston_surface *main_surface; - - main_surface = weston_surface_get_main_surface(surface); - move_surface_to_workspace(shell, main_surface, workspace); -} - -static const struct workspace_manager_interface workspace_manager_implementation = { - workspace_manager_move_surface, -}; - -static void -unbind_resource(struct wl_resource *resource) -{ - wl_list_remove(wl_resource_get_link(resource)); -} - -static void -bind_workspace_manager(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct desktop_shell *shell = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, - &workspace_manager_interface, 1, id); - - if (resource == NULL) { - weston_log("couldn't add workspace manager object"); - return; - } - - wl_resource_set_implementation(resource, - &workspace_manager_implementation, - shell, unbind_resource); - wl_list_insert(&shell->workspaces.client_list, - wl_resource_get_link(resource)); - - workspace_manager_send_state(resource, - shell->workspaces.current, - shell->workspaces.num); -} - -static void -touch_move_grab_down(struct weston_touch_grab *grab, uint32_t time, - int touch_id, wl_fixed_t sx, wl_fixed_t sy) -{ -} - -static void -touch_move_grab_up(struct weston_touch_grab *grab, uint32_t time, int touch_id) -{ - struct weston_touch_move_grab *move = - (struct weston_touch_move_grab *) container_of( - grab, struct shell_touch_grab, grab); - - if (grab->touch->seat->num_tp == 0) { - shell_touch_grab_end(&move->base); - free(move); - } -} - -static void -touch_move_grab_motion(struct weston_touch_grab *grab, uint32_t time, - int touch_id, wl_fixed_t sx, wl_fixed_t sy) -{ - struct weston_touch_move_grab *move = (struct weston_touch_move_grab *) grab; - struct shell_surface *shsurf = move->base.shsurf; - struct weston_surface *es; - int dx = wl_fixed_to_int(grab->touch->grab_x + move->dx); - int dy = wl_fixed_to_int(grab->touch->grab_y + move->dy); - - if (!shsurf) - return; - - es = shsurf->surface; - - weston_surface_configure(es, dx, dy, - es->geometry.width, es->geometry.height); - - weston_compositor_schedule_repaint(es->compositor); -} - -static void -touch_move_grab_cancel(struct weston_touch_grab *grab) -{ - struct weston_touch_move_grab *move = - (struct weston_touch_move_grab *) container_of( - grab, struct shell_touch_grab, grab); - - shell_touch_grab_end(&move->base); - free(move); -} - -static const struct weston_touch_grab_interface touch_move_grab_interface = { - touch_move_grab_down, - touch_move_grab_up, - touch_move_grab_motion, - touch_move_grab_cancel, -}; - -static int -surface_touch_move(struct shell_surface *shsurf, struct weston_seat *seat) -{ - struct weston_touch_move_grab *move; - - if (!shsurf) - return -1; - - if (shsurf->type == SHELL_SURFACE_FULLSCREEN) - return 0; - - move = malloc(sizeof *move); - if (!move) - return -1; - - move->dx = wl_fixed_from_double(shsurf->surface->geometry.x) - - seat->touch->grab_x; - move->dy = wl_fixed_from_double(shsurf->surface->geometry.y) - - seat->touch->grab_y; - - shell_touch_grab_start(&move->base, &touch_move_grab_interface, shsurf, - seat->touch); - - return 0; -} - -static void -noop_grab_focus(struct weston_pointer_grab *grab) -{ -} - -static void -move_grab_motion(struct weston_pointer_grab *grab, uint32_t time) -{ - struct weston_move_grab *move = (struct weston_move_grab *) grab; - struct weston_pointer *pointer = grab->pointer; - struct shell_surface *shsurf = move->base.shsurf; - struct weston_surface *es; - int dx = wl_fixed_to_int(pointer->x + move->dx); - int dy = wl_fixed_to_int(pointer->y + move->dy); - - if (!shsurf) - return; - - es = shsurf->surface; - - weston_surface_configure(es, dx, dy, - es->geometry.width, es->geometry.height); - - weston_compositor_schedule_repaint(es->compositor); -} - -static void -move_grab_button(struct weston_pointer_grab *grab, - uint32_t time, uint32_t button, uint32_t state_w) -{ - struct shell_grab *shell_grab = container_of(grab, struct shell_grab, - grab); - struct weston_pointer *pointer = grab->pointer; - enum wl_pointer_button_state state = state_w; - - if (pointer->button_count == 0 && - state == WL_POINTER_BUTTON_STATE_RELEASED) { - shell_grab_end(shell_grab); - free(grab); - } -} - -static void -move_grab_cancel(struct weston_pointer_grab *grab) -{ - struct shell_grab *shell_grab = - container_of(grab, struct shell_grab, grab); - - shell_grab_end(shell_grab); - free(grab); -} - -static const struct weston_pointer_grab_interface move_grab_interface = { - noop_grab_focus, - move_grab_motion, - move_grab_button, - move_grab_cancel, -}; - -static int -surface_move(struct shell_surface *shsurf, struct weston_seat *seat) -{ - struct weston_move_grab *move; - - if (!shsurf) - return -1; - - if (shsurf->type == SHELL_SURFACE_FULLSCREEN) - return 0; - - move = malloc(sizeof *move); - if (!move) - return -1; - - move->dx = wl_fixed_from_double(shsurf->surface->geometry.x) - - seat->pointer->grab_x; - move->dy = wl_fixed_from_double(shsurf->surface->geometry.y) - - seat->pointer->grab_y; - - shell_grab_start(&move->base, &move_grab_interface, shsurf, - seat->pointer, DESKTOP_SHELL_CURSOR_MOVE); - - return 0; -} - -static void -shell_surface_move(struct wl_client *client, struct wl_resource *resource, - struct wl_resource *seat_resource, uint32_t serial) -{ - struct weston_seat *seat = wl_resource_get_user_data(seat_resource); - struct shell_surface *shsurf = wl_resource_get_user_data(resource); - struct weston_surface *surface; - - if (seat->pointer && - seat->pointer->button_count > 0 && - seat->pointer->grab_serial == serial) { - surface = weston_surface_get_main_surface(seat->pointer->focus); - if ((surface == shsurf->surface) && - (surface_move(shsurf, seat) < 0)) - wl_resource_post_no_memory(resource); - } else if (seat->touch && - seat->touch->grab_serial == serial) { - surface = weston_surface_get_main_surface(seat->touch->focus); - if ((surface == shsurf->surface) && - (surface_touch_move(shsurf, seat) < 0)) - wl_resource_post_no_memory(resource); - } -} - -struct weston_resize_grab { - struct shell_grab base; - uint32_t edges; - int32_t width, height; -}; - -static void -resize_grab_motion(struct weston_pointer_grab *grab, uint32_t time) -{ - struct weston_resize_grab *resize = (struct weston_resize_grab *) grab; - struct weston_pointer *pointer = grab->pointer; - struct shell_surface *shsurf = resize->base.shsurf; - int32_t width, height; - wl_fixed_t from_x, from_y; - wl_fixed_t to_x, to_y; - - if (!shsurf) - return; - - weston_surface_from_global_fixed(shsurf->surface, - pointer->grab_x, pointer->grab_y, - &from_x, &from_y); - weston_surface_from_global_fixed(shsurf->surface, - pointer->x, pointer->y, &to_x, &to_y); - - width = resize->width; - if (resize->edges & WL_SHELL_SURFACE_RESIZE_LEFT) { - width += wl_fixed_to_int(from_x - to_x); - } else if (resize->edges & WL_SHELL_SURFACE_RESIZE_RIGHT) { - width += wl_fixed_to_int(to_x - from_x); - } - - height = resize->height; - if (resize->edges & WL_SHELL_SURFACE_RESIZE_TOP) { - height += wl_fixed_to_int(from_y - to_y); - } else if (resize->edges & WL_SHELL_SURFACE_RESIZE_BOTTOM) { - height += wl_fixed_to_int(to_y - from_y); - } - - shsurf->client->send_configure(shsurf->surface, - resize->edges, width, height); -} - -static void -send_configure(struct weston_surface *surface, - uint32_t edges, int32_t width, int32_t height) -{ - struct shell_surface *shsurf = get_shell_surface(surface); - - wl_shell_surface_send_configure(shsurf->resource, - edges, width, height); -} - -static const struct weston_shell_client shell_client = { - send_configure -}; - -static void -resize_grab_button(struct weston_pointer_grab *grab, - uint32_t time, uint32_t button, uint32_t state_w) -{ - struct weston_resize_grab *resize = (struct weston_resize_grab *) grab; - struct weston_pointer *pointer = grab->pointer; - enum wl_pointer_button_state state = state_w; - - if (pointer->button_count == 0 && - state == WL_POINTER_BUTTON_STATE_RELEASED) { - shell_grab_end(&resize->base); - free(grab); - } -} - -static void -resize_grab_cancel(struct weston_pointer_grab *grab) -{ - struct weston_resize_grab *resize = (struct weston_resize_grab *) grab; - - shell_grab_end(&resize->base); - free(grab); -} - -static const struct weston_pointer_grab_interface resize_grab_interface = { - noop_grab_focus, - resize_grab_motion, - resize_grab_button, - resize_grab_cancel, -}; - -/* - * Returns the bounding box of a surface and all its sub-surfaces, - * in the surface coordinates system. */ -static void -surface_subsurfaces_boundingbox(struct weston_surface *surface, int32_t *x, - int32_t *y, int32_t *w, int32_t *h) { - pixman_region32_t region; - pixman_box32_t *box; - struct weston_subsurface *subsurface; - - pixman_region32_init_rect(®ion, 0, 0, - surface->geometry.width, - surface->geometry.height); - - wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) { - pixman_region32_union_rect(®ion, ®ion, - subsurface->position.x, - subsurface->position.y, - subsurface->surface->geometry.width, - subsurface->surface->geometry.height); - } - - box = pixman_region32_extents(®ion); - if (x) - *x = box->x1; - if (y) - *y = box->y1; - if (w) - *w = box->x2 - box->x1; - if (h) - *h = box->y2 - box->y1; - - pixman_region32_fini(®ion); -} - -static int -surface_resize(struct shell_surface *shsurf, - struct weston_seat *seat, uint32_t edges) -{ - struct weston_resize_grab *resize; - - if (shsurf->type == SHELL_SURFACE_FULLSCREEN || - shsurf->type == SHELL_SURFACE_MAXIMIZED) - return 0; - - if (edges == 0 || edges > 15 || - (edges & 3) == 3 || (edges & 12) == 12) - return 0; - - resize = malloc(sizeof *resize); - if (!resize) - return -1; - - resize->edges = edges; - surface_subsurfaces_boundingbox(shsurf->surface, NULL, NULL, - &resize->width, &resize->height); - - shell_grab_start(&resize->base, &resize_grab_interface, shsurf, - seat->pointer, edges); - - return 0; -} - -static void -shell_surface_resize(struct wl_client *client, struct wl_resource *resource, - struct wl_resource *seat_resource, uint32_t serial, - uint32_t edges) -{ - struct weston_seat *seat = wl_resource_get_user_data(seat_resource); - struct shell_surface *shsurf = wl_resource_get_user_data(resource); - struct weston_surface *surface; - - if (shsurf->type == SHELL_SURFACE_FULLSCREEN) - return; - - surface = weston_surface_get_main_surface(seat->pointer->focus); - if (seat->pointer->button_count == 0 || - seat->pointer->grab_serial != serial || - surface != shsurf->surface) - return; - - if (surface_resize(shsurf, seat, edges) < 0) - wl_resource_post_no_memory(resource); -} - -static void -end_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer); - -static void -busy_cursor_grab_focus(struct weston_pointer_grab *base) -{ - struct shell_grab *grab = (struct shell_grab *) base; - struct weston_pointer *pointer = base->pointer; - struct weston_surface *surface; - wl_fixed_t sx, sy; - - surface = weston_compositor_pick_surface(pointer->seat->compositor, - pointer->x, pointer->y, - &sx, &sy); - - if (!grab->shsurf || grab->shsurf->surface != surface) - end_busy_cursor(grab->shsurf, pointer); -} - -static void -busy_cursor_grab_motion(struct weston_pointer_grab *grab, uint32_t time) -{ -} - -static void -busy_cursor_grab_button(struct weston_pointer_grab *base, - uint32_t time, uint32_t button, uint32_t state) -{ - struct shell_grab *grab = (struct shell_grab *) base; - struct shell_surface *shsurf = grab->shsurf; - struct weston_seat *seat = grab->grab.pointer->seat; - - if (shsurf && button == BTN_LEFT && state) { - activate(shsurf->shell, shsurf->surface, seat); - surface_move(shsurf, seat); - } else if (shsurf && button == BTN_RIGHT && state) { - activate(shsurf->shell, shsurf->surface, seat); - surface_rotate(shsurf, seat); - } -} - -static void -busy_cursor_grab_cancel(struct weston_pointer_grab *base) -{ - struct shell_grab *grab = (struct shell_grab *) base; - - shell_grab_end(grab); - free(grab); -} - -static const struct weston_pointer_grab_interface busy_cursor_grab_interface = { - busy_cursor_grab_focus, - busy_cursor_grab_motion, - busy_cursor_grab_button, - busy_cursor_grab_cancel, -}; - -static void -set_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer) -{ - struct shell_grab *grab; - - grab = malloc(sizeof *grab); - if (!grab) - return; - - shell_grab_start(grab, &busy_cursor_grab_interface, shsurf, pointer, - DESKTOP_SHELL_CURSOR_BUSY); -} - -static void -end_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer) -{ - struct shell_grab *grab = (struct shell_grab *) pointer->grab; - - if (grab->grab.interface == &busy_cursor_grab_interface && - grab->shsurf == shsurf) { - shell_grab_end(grab); - free(grab); - } -} - -static void -ping_timer_destroy(struct shell_surface *shsurf) -{ - if (!shsurf || !shsurf->ping_timer) - return; - - if (shsurf->ping_timer->source) - wl_event_source_remove(shsurf->ping_timer->source); - - free(shsurf->ping_timer); - shsurf->ping_timer = NULL; -} - -static int -ping_timeout_handler(void *data) -{ - struct shell_surface *shsurf = data; - struct weston_seat *seat; - - /* Client is not responding */ - shsurf->unresponsive = 1; - - wl_list_for_each(seat, &shsurf->surface->compositor->seat_list, link) - if (seat->pointer->focus == shsurf->surface) - set_busy_cursor(shsurf, seat->pointer); - - return 1; -} - -static void -ping_handler(struct weston_surface *surface, uint32_t serial) -{ - struct shell_surface *shsurf = get_shell_surface(surface); - struct wl_event_loop *loop; - int ping_timeout = 200; - - if (!shsurf) - return; - if (!shsurf->resource) - return; - - if (shsurf->surface == shsurf->shell->grab_surface) - return; - - if (!shsurf->ping_timer) { - shsurf->ping_timer = malloc(sizeof *shsurf->ping_timer); - if (!shsurf->ping_timer) - return; - - shsurf->ping_timer->serial = serial; - loop = wl_display_get_event_loop(surface->compositor->wl_display); - shsurf->ping_timer->source = - wl_event_loop_add_timer(loop, ping_timeout_handler, shsurf); - wl_event_source_timer_update(shsurf->ping_timer->source, ping_timeout); - - wl_shell_surface_send_ping(shsurf->resource, serial); - } -} - -static void -handle_pointer_focus(struct wl_listener *listener, void *data) -{ - struct weston_pointer *pointer = data; - struct weston_surface *surface = pointer->focus; - struct weston_compositor *compositor; - struct shell_surface *shsurf; - uint32_t serial; - - if (!surface) - return; - - compositor = surface->compositor; - shsurf = get_shell_surface(surface); - - if (shsurf && shsurf->unresponsive) { - set_busy_cursor(shsurf, pointer); - } else { - serial = wl_display_next_serial(compositor->wl_display); - ping_handler(surface, serial); - } -} - -static void -create_pointer_focus_listener(struct weston_seat *seat) -{ - struct wl_listener *listener; - - if (!seat->pointer) - return; - - listener = malloc(sizeof *listener); - listener->notify = handle_pointer_focus; - wl_signal_add(&seat->pointer->focus_signal, listener); -} - -static void -shell_surface_pong(struct wl_client *client, struct wl_resource *resource, - uint32_t serial) -{ - struct shell_surface *shsurf = wl_resource_get_user_data(resource); - struct weston_seat *seat; - struct weston_compositor *ec = shsurf->surface->compositor; - - if (shsurf->ping_timer == NULL) - /* Just ignore unsolicited pong. */ - return; - - if (shsurf->ping_timer->serial == serial) { - shsurf->unresponsive = 0; - wl_list_for_each(seat, &ec->seat_list, link) { - if(seat->pointer) - end_busy_cursor(shsurf, seat->pointer); - } - ping_timer_destroy(shsurf); - } -} - -static void -set_title(struct shell_surface *shsurf, const char *title) -{ - free(shsurf->title); - shsurf->title = strdup(title); -} - -static void -shell_surface_set_title(struct wl_client *client, - struct wl_resource *resource, const char *title) -{ - struct shell_surface *shsurf = wl_resource_get_user_data(resource); - - set_title(shsurf, title); -} - -static void -shell_surface_set_class(struct wl_client *client, - struct wl_resource *resource, const char *class) -{ - struct shell_surface *shsurf = wl_resource_get_user_data(resource); - - free(shsurf->class); - shsurf->class = strdup(class); -} - -static struct weston_output * -get_default_output(struct weston_compositor *compositor) -{ - return container_of(compositor->output_list.next, - struct weston_output, link); -} - -static void -restore_output_mode(struct weston_output *output) -{ - if (output->current_mode != output->original_mode || - (int32_t)output->current_scale != output->original_scale) - weston_output_switch_mode(output, - output->original_mode, - output->original_scale, - WESTON_MODE_SWITCH_RESTORE_NATIVE); -} - -static void -restore_all_output_modes(struct weston_compositor *compositor) -{ - struct weston_output *output; - - wl_list_for_each(output, &compositor->output_list, link) - restore_output_mode(output); -} - -static void -shell_unset_fullscreen(struct shell_surface *shsurf) -{ - struct workspace *ws; - /* undo all fullscreen things here */ - if (shsurf->fullscreen.type == WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER && - shell_surface_is_top_fullscreen(shsurf)) { - restore_output_mode(shsurf->fullscreen_output); - } - shsurf->fullscreen.type = WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT; - shsurf->fullscreen.framerate = 0; - wl_list_remove(&shsurf->fullscreen.transform.link); - wl_list_init(&shsurf->fullscreen.transform.link); - if (shsurf->fullscreen.black_surface) - weston_surface_destroy(shsurf->fullscreen.black_surface); - shsurf->fullscreen.black_surface = NULL; - shsurf->fullscreen_output = NULL; - weston_surface_set_position(shsurf->surface, - shsurf->saved_x, shsurf->saved_y); - if (shsurf->saved_rotation_valid) { - wl_list_insert(&shsurf->surface->geometry.transformation_list, - &shsurf->rotation.transform.link); - shsurf->saved_rotation_valid = false; - } - - ws = get_current_workspace(shsurf->shell); - wl_list_remove(&shsurf->surface->layer_link); - wl_list_insert(&ws->layer.surface_list, &shsurf->surface->layer_link); -} - -static void -shell_unset_maximized(struct shell_surface *shsurf) -{ - struct workspace *ws; - /* undo all maximized things here */ - shsurf->output = get_default_output(shsurf->surface->compositor); - weston_surface_set_position(shsurf->surface, - shsurf->saved_x, - shsurf->saved_y); - - if (shsurf->saved_rotation_valid) { - wl_list_insert(&shsurf->surface->geometry.transformation_list, - &shsurf->rotation.transform.link); - shsurf->saved_rotation_valid = false; - } - - ws = get_current_workspace(shsurf->shell); - wl_list_remove(&shsurf->surface->layer_link); - wl_list_insert(&ws->layer.surface_list, &shsurf->surface->layer_link); -} - -static int -reset_shell_surface_type(struct shell_surface *surface) -{ - switch (surface->type) { - case SHELL_SURFACE_FULLSCREEN: - shell_unset_fullscreen(surface); - break; - case SHELL_SURFACE_MAXIMIZED: - shell_unset_maximized(surface); - break; - case SHELL_SURFACE_NONE: - case SHELL_SURFACE_TOPLEVEL: - case SHELL_SURFACE_TRANSIENT: - case SHELL_SURFACE_POPUP: - case SHELL_SURFACE_XWAYLAND: - break; - } - - surface->type = SHELL_SURFACE_NONE; - return 0; -} - -static void -set_surface_type(struct shell_surface *shsurf) -{ - struct weston_surface *surface = shsurf->surface; - struct weston_surface *pes = shsurf->parent; - - reset_shell_surface_type(shsurf); - - shsurf->type = shsurf->next_type; - shsurf->next_type = SHELL_SURFACE_NONE; - - switch (shsurf->type) { - case SHELL_SURFACE_TOPLEVEL: - break; - case SHELL_SURFACE_TRANSIENT: - weston_surface_set_position(surface, - pes->geometry.x + shsurf->transient.x, - pes->geometry.y + shsurf->transient.y); - break; - - case SHELL_SURFACE_MAXIMIZED: - case SHELL_SURFACE_FULLSCREEN: - shsurf->saved_x = surface->geometry.x; - shsurf->saved_y = surface->geometry.y; - shsurf->saved_position_valid = true; - - if (!wl_list_empty(&shsurf->rotation.transform.link)) { - wl_list_remove(&shsurf->rotation.transform.link); - wl_list_init(&shsurf->rotation.transform.link); - weston_surface_geometry_dirty(shsurf->surface); - shsurf->saved_rotation_valid = true; - } - break; - - case SHELL_SURFACE_XWAYLAND: - weston_surface_set_position(surface, shsurf->transient.x, - shsurf->transient.y); - break; - - default: - break; - } -} - -static void -set_toplevel(struct shell_surface *shsurf) -{ - shsurf->next_type = SHELL_SURFACE_TOPLEVEL; -} - -static void -shell_surface_set_toplevel(struct wl_client *client, - struct wl_resource *resource) -{ - struct shell_surface *surface = wl_resource_get_user_data(resource); - - set_toplevel(surface); -} - -static void -set_transient(struct shell_surface *shsurf, - struct weston_surface *parent, int x, int y, uint32_t flags) -{ - /* assign to parents output */ - shsurf->parent = parent; - shsurf->transient.x = x; - shsurf->transient.y = y; - shsurf->transient.flags = flags; - shsurf->next_type = SHELL_SURFACE_TRANSIENT; -} - -static void -shell_surface_set_transient(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *parent_resource, - int x, int y, uint32_t flags) -{ - struct shell_surface *shsurf = wl_resource_get_user_data(resource); - struct weston_surface *parent = - wl_resource_get_user_data(parent_resource); - - set_transient(shsurf, parent, x, y, flags); -} - -static struct desktop_shell * -shell_surface_get_shell(struct shell_surface *shsurf) -{ - return shsurf->shell; -} - -static int -get_output_panel_height(struct desktop_shell *shell, - struct weston_output *output) -{ - struct weston_surface *surface; - int panel_height = 0; - - if (!output) - return 0; - - wl_list_for_each(surface, &shell->panel_layer.surface_list, layer_link) { - if (surface->output == output) { - panel_height = surface->geometry.height; - break; - } - } - - return panel_height; -} - -static void -shell_surface_set_maximized(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *output_resource ) -{ - struct shell_surface *shsurf = wl_resource_get_user_data(resource); - struct weston_surface *es = shsurf->surface; - struct desktop_shell *shell = NULL; - uint32_t edges = 0, panel_height = 0; - - /* get the default output, if the client set it as NULL - check whether the ouput is available */ - if (output_resource) - shsurf->output = wl_resource_get_user_data(output_resource); - else if (es->output) - shsurf->output = es->output; - else - shsurf->output = get_default_output(es->compositor); - - shell = shell_surface_get_shell(shsurf); - panel_height = get_output_panel_height(shell, shsurf->output); - edges = WL_SHELL_SURFACE_RESIZE_TOP|WL_SHELL_SURFACE_RESIZE_LEFT; - - shsurf->client->send_configure(shsurf->surface, edges, - shsurf->output->width, - shsurf->output->height - panel_height); - - shsurf->next_type = SHELL_SURFACE_MAXIMIZED; -} - -static void -black_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height); - -static struct weston_surface * -create_black_surface(struct weston_compositor *ec, - struct weston_surface *fs_surface, - float x, float y, int w, int h) -{ - struct weston_surface *surface = NULL; - - surface = weston_surface_create(ec); - if (surface == NULL) { - weston_log("no memory\n"); - return NULL; - } - - surface->configure = black_surface_configure; - surface->configure_private = fs_surface; - weston_surface_configure(surface, x, y, w, h); - weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1); - pixman_region32_fini(&surface->opaque); - pixman_region32_init_rect(&surface->opaque, 0, 0, w, h); - pixman_region32_fini(&surface->input); - pixman_region32_init_rect(&surface->input, 0, 0, w, h); - - return surface; -} - -/* Create black surface and append it to the associated fullscreen surface. - * Handle size dismatch and positioning according to the method. */ -static void -shell_configure_fullscreen(struct shell_surface *shsurf) -{ - struct weston_output *output = shsurf->fullscreen_output; - struct weston_surface *surface = shsurf->surface; - struct weston_matrix *matrix; - float scale, output_aspect, surface_aspect, x, y; - int32_t surf_x, surf_y, surf_width, surf_height; - - if (shsurf->fullscreen.type != WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER) - restore_output_mode(output); - - if (!shsurf->fullscreen.black_surface) - shsurf->fullscreen.black_surface = - create_black_surface(surface->compositor, - surface, - output->x, output->y, - output->width, - output->height); - - wl_list_remove(&shsurf->fullscreen.black_surface->layer_link); - wl_list_insert(&surface->layer_link, - &shsurf->fullscreen.black_surface->layer_link); - shsurf->fullscreen.black_surface->output = output; - - surface_subsurfaces_boundingbox(surface, &surf_x, &surf_y, - &surf_width, &surf_height); - - switch (shsurf->fullscreen.type) { - case WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT: - if (surface->buffer_ref.buffer) - center_on_output(surface, shsurf->fullscreen_output); - break; - case WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE: - /* 1:1 mapping between surface and output dimensions */ - if (output->width == surf_width && - output->height == surf_height) { - weston_surface_set_position(surface, output->x - surf_x, - output->y - surf_y); - break; - } - - matrix = &shsurf->fullscreen.transform.matrix; - weston_matrix_init(matrix); - - output_aspect = (float) output->width / - (float) output->height; - surface_aspect = (float) surface->geometry.width / - (float) surface->geometry.height; - if (output_aspect < surface_aspect) - scale = (float) output->width / - (float) surf_width; - else - scale = (float) output->height / - (float) surf_height; - - weston_matrix_scale(matrix, scale, scale, 1); - wl_list_remove(&shsurf->fullscreen.transform.link); - wl_list_insert(&surface->geometry.transformation_list, - &shsurf->fullscreen.transform.link); - x = output->x + (output->width - surf_width * scale) / 2 - surf_x; - y = output->y + (output->height - surf_height * scale) / 2 - surf_y; - weston_surface_set_position(surface, x, y); - - break; - case WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER: - if (shell_surface_is_top_fullscreen(shsurf)) { - struct weston_mode mode = {0, - surf_width * surface->buffer_scale, - surf_height * surface->buffer_scale, - shsurf->fullscreen.framerate}; - - if (weston_output_switch_mode(output, &mode, surface->buffer_scale, - WESTON_MODE_SWITCH_SET_TEMPORARY) == 0) { - weston_surface_set_position(surface, - output->x - surf_x, - output->y - surf_y); - weston_surface_configure(shsurf->fullscreen.black_surface, - output->x - surf_x, - output->y - surf_y, - output->width, - output->height); - break; - } else { - restore_output_mode(output); - center_on_output(surface, output); - } - } - break; - case WL_SHELL_SURFACE_FULLSCREEN_METHOD_FILL: - center_on_output(surface, output); - break; - default: - break; - } -} - -/* make the fullscreen and black surface at the top */ -static void -shell_stack_fullscreen(struct shell_surface *shsurf) -{ - struct weston_output *output = shsurf->fullscreen_output; - struct weston_surface *surface = shsurf->surface; - struct desktop_shell *shell = shell_surface_get_shell(shsurf); - - wl_list_remove(&surface->layer_link); - wl_list_insert(&shell->fullscreen_layer.surface_list, - &surface->layer_link); - weston_surface_damage(surface); - - if (!shsurf->fullscreen.black_surface) - shsurf->fullscreen.black_surface = - create_black_surface(surface->compositor, - surface, - output->x, output->y, - output->width, - output->height); - - wl_list_remove(&shsurf->fullscreen.black_surface->layer_link); - wl_list_insert(&surface->layer_link, - &shsurf->fullscreen.black_surface->layer_link); - weston_surface_damage(shsurf->fullscreen.black_surface); -} - -static void -shell_map_fullscreen(struct shell_surface *shsurf) -{ - shell_stack_fullscreen(shsurf); - shell_configure_fullscreen(shsurf); -} - -static void -set_fullscreen(struct shell_surface *shsurf, - uint32_t method, - uint32_t framerate, - struct weston_output *output) -{ - struct weston_surface *es = shsurf->surface; - - if (output) - shsurf->output = output; - else if (es->output) - shsurf->output = es->output; - else - shsurf->output = get_default_output(es->compositor); - - shsurf->fullscreen_output = shsurf->output; - shsurf->fullscreen.type = method; - shsurf->fullscreen.framerate = framerate; - shsurf->next_type = SHELL_SURFACE_FULLSCREEN; - - shsurf->client->send_configure(shsurf->surface, 0, - shsurf->output->width, - shsurf->output->height); -} - -static void -shell_surface_set_fullscreen(struct wl_client *client, - struct wl_resource *resource, - uint32_t method, - uint32_t framerate, - struct wl_resource *output_resource) -{ - struct shell_surface *shsurf = wl_resource_get_user_data(resource); - struct weston_output *output; - - if (output_resource) - output = wl_resource_get_user_data(output_resource); - else - output = NULL; - - set_fullscreen(shsurf, method, framerate, output); -} - -static void -set_xwayland(struct shell_surface *shsurf, int x, int y, uint32_t flags) -{ - /* XXX: using the same fields for transient type */ - shsurf->transient.x = x; - shsurf->transient.y = y; - shsurf->transient.flags = flags; - shsurf->next_type = SHELL_SURFACE_XWAYLAND; -} - -static const struct weston_pointer_grab_interface popup_grab_interface; - -static void -destroy_shell_seat(struct wl_listener *listener, void *data) -{ - struct shell_seat *shseat = - container_of(listener, - struct shell_seat, seat_destroy_listener); - struct shell_surface *shsurf, *prev = NULL; - - if (shseat->popup_grab.grab.interface == &popup_grab_interface) { - weston_pointer_end_grab(shseat->popup_grab.grab.pointer); - shseat->popup_grab.client = NULL; - - wl_list_for_each(shsurf, &shseat->popup_grab.surfaces_list, popup.grab_link) { - shsurf->popup.shseat = NULL; - if (prev) { - wl_list_init(&prev->popup.grab_link); - } - prev = shsurf; - } - wl_list_init(&prev->popup.grab_link); - } - - wl_list_remove(&shseat->seat_destroy_listener.link); - free(shseat); -} - -static struct shell_seat * -create_shell_seat(struct weston_seat *seat) -{ - struct shell_seat *shseat; - - shseat = calloc(1, sizeof *shseat); - if (!shseat) { - weston_log("no memory to allocate shell seat\n"); - return NULL; - } - - shseat->seat = seat; - wl_list_init(&shseat->popup_grab.surfaces_list); - - shseat->seat_destroy_listener.notify = destroy_shell_seat; - wl_signal_add(&seat->destroy_signal, - &shseat->seat_destroy_listener); - - return shseat; -} - -static struct shell_seat * -get_shell_seat(struct weston_seat *seat) -{ - struct wl_listener *listener; - - listener = wl_signal_get(&seat->destroy_signal, destroy_shell_seat); - if (listener == NULL) - return create_shell_seat(seat); - - return container_of(listener, - struct shell_seat, seat_destroy_listener); -} - -static void -popup_grab_focus(struct weston_pointer_grab *grab) -{ - struct weston_pointer *pointer = grab->pointer; - struct weston_surface *surface; - struct shell_seat *shseat = - container_of(grab, struct shell_seat, popup_grab.grab); - struct wl_client *client = shseat->popup_grab.client; - wl_fixed_t sx, sy; - - surface = weston_compositor_pick_surface(pointer->seat->compositor, - pointer->x, pointer->y, - &sx, &sy); - - if (surface && wl_resource_get_client(surface->resource) == client) { - weston_pointer_set_focus(pointer, surface, sx, sy); - } else { - weston_pointer_set_focus(pointer, NULL, - wl_fixed_from_int(0), - wl_fixed_from_int(0)); - } -} - -static void -popup_grab_motion(struct weston_pointer_grab *grab, uint32_t time) -{ - struct weston_pointer *pointer = grab->pointer; - struct wl_resource *resource; - wl_fixed_t sx, sy; - - wl_resource_for_each(resource, &pointer->focus_resource_list) { - weston_surface_from_global_fixed(pointer->focus, - pointer->x, pointer->y, - &sx, &sy); - wl_pointer_send_motion(resource, time, sx, sy); - } -} - -static void -popup_grab_button(struct weston_pointer_grab *grab, - uint32_t time, uint32_t button, uint32_t state_w) -{ - struct wl_resource *resource; - struct shell_seat *shseat = - container_of(grab, struct shell_seat, popup_grab.grab); - struct wl_display *display = shseat->seat->compositor->wl_display; - enum wl_pointer_button_state state = state_w; - uint32_t serial; - struct wl_list *resource_list; - - resource_list = &grab->pointer->focus_resource_list; - if (!wl_list_empty(resource_list)) { - serial = wl_display_get_serial(display); - wl_resource_for_each(resource, resource_list) { - wl_pointer_send_button(resource, serial, - time, button, state); - } - } else if (state == WL_POINTER_BUTTON_STATE_RELEASED && - (shseat->popup_grab.initial_up || - time - shseat->seat->pointer->grab_time > 500)) { - popup_grab_end(grab->pointer); - } - - if (state == WL_POINTER_BUTTON_STATE_RELEASED) - shseat->popup_grab.initial_up = 1; -} - -static void -popup_grab_cancel(struct weston_pointer_grab *grab) -{ - popup_grab_end(grab->pointer); -} - -static const struct weston_pointer_grab_interface popup_grab_interface = { - popup_grab_focus, - popup_grab_motion, - popup_grab_button, - popup_grab_cancel, -}; - -static void -popup_grab_end(struct weston_pointer *pointer) -{ - struct weston_pointer_grab *grab = pointer->grab; - struct shell_seat *shseat = - container_of(grab, struct shell_seat, popup_grab.grab); - struct shell_surface *shsurf; - struct shell_surface *prev = NULL; - - if (pointer->grab->interface == &popup_grab_interface) { - weston_pointer_end_grab(grab->pointer); - shseat->popup_grab.client = NULL; - shseat->popup_grab.grab.interface = NULL; - assert(!wl_list_empty(&shseat->popup_grab.surfaces_list)); - /* Send the popup_done event to all the popups open */ - wl_list_for_each(shsurf, &shseat->popup_grab.surfaces_list, popup.grab_link) { - wl_shell_surface_send_popup_done(shsurf->resource); - shsurf->popup.shseat = NULL; - if (prev) { - wl_list_init(&prev->popup.grab_link); - } - prev = shsurf; - } - wl_list_init(&prev->popup.grab_link); - wl_list_init(&shseat->popup_grab.surfaces_list); - } -} - -static void -add_popup_grab(struct shell_surface *shsurf, struct shell_seat *shseat) -{ - struct weston_seat *seat = shseat->seat; - - if (wl_list_empty(&shseat->popup_grab.surfaces_list)) { - shseat->popup_grab.client = wl_resource_get_client(shsurf->resource); - shseat->popup_grab.grab.interface = &popup_grab_interface; - /* We must make sure here that this popup was opened after - * a mouse press, and not just by moving around with other - * popups already open. */ - if (shseat->seat->pointer->button_count > 0) - shseat->popup_grab.initial_up = 0; - - wl_list_insert(&shseat->popup_grab.surfaces_list, &shsurf->popup.grab_link); - weston_pointer_start_grab(seat->pointer, &shseat->popup_grab.grab); - } else { - wl_list_insert(&shseat->popup_grab.surfaces_list, &shsurf->popup.grab_link); - } -} - -static void -remove_popup_grab(struct shell_surface *shsurf) -{ - struct shell_seat *shseat = shsurf->popup.shseat; - - wl_list_remove(&shsurf->popup.grab_link); - wl_list_init(&shsurf->popup.grab_link); - if (wl_list_empty(&shseat->popup_grab.surfaces_list)) { - weston_pointer_end_grab(shseat->popup_grab.grab.pointer); - shseat->popup_grab.grab.interface = NULL; - } -} - -static void -shell_map_popup(struct shell_surface *shsurf) -{ - struct shell_seat *shseat = shsurf->popup.shseat; - struct weston_surface *es = shsurf->surface; - struct weston_surface *parent = shsurf->parent; - - es->output = parent->output; - - weston_surface_set_transform_parent(es, parent); - weston_surface_set_position(es, shsurf->popup.x, shsurf->popup.y); - weston_surface_update_transform(es); - - if (shseat->seat->pointer->grab_serial == shsurf->popup.serial) { - add_popup_grab(shsurf, shseat); - } else { - wl_shell_surface_send_popup_done(shsurf->resource); - shseat->popup_grab.client = NULL; - } -} - -static void -shell_surface_set_popup(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *seat_resource, - uint32_t serial, - struct wl_resource *parent_resource, - int32_t x, int32_t y, uint32_t flags) -{ - struct shell_surface *shsurf = wl_resource_get_user_data(resource); - - shsurf->type = SHELL_SURFACE_POPUP; - shsurf->parent = wl_resource_get_user_data(parent_resource); - shsurf->popup.shseat = get_shell_seat(wl_resource_get_user_data(seat_resource)); - shsurf->popup.serial = serial; - shsurf->popup.x = x; - shsurf->popup.y = y; -} - -static const struct wl_shell_surface_interface shell_surface_implementation = { - shell_surface_pong, - shell_surface_move, - shell_surface_resize, - shell_surface_set_toplevel, - shell_surface_set_transient, - shell_surface_set_fullscreen, - shell_surface_set_popup, - shell_surface_set_maximized, - shell_surface_set_title, - shell_surface_set_class -}; - -static void -destroy_shell_surface(struct shell_surface *shsurf) -{ - wl_signal_emit(&shsurf->destroy_signal, shsurf); - - if (!wl_list_empty(&shsurf->popup.grab_link)) { - remove_popup_grab(shsurf); - } - - if (shsurf->fullscreen.type == WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER && - shell_surface_is_top_fullscreen(shsurf)) - restore_output_mode (shsurf->fullscreen_output); - - if (shsurf->fullscreen.black_surface) - weston_surface_destroy(shsurf->fullscreen.black_surface); - - /* As destroy_resource() use wl_list_for_each_safe(), - * we can always remove the listener. - */ - wl_list_remove(&shsurf->surface_destroy_listener.link); - shsurf->surface->configure = NULL; - ping_timer_destroy(shsurf); - free(shsurf->title); - - wl_list_remove(&shsurf->link); - free(shsurf); -} - -static void -shell_destroy_shell_surface(struct wl_resource *resource) -{ - struct shell_surface *shsurf = wl_resource_get_user_data(resource); - - destroy_shell_surface(shsurf); -} - -static void -shell_handle_surface_destroy(struct wl_listener *listener, void *data) -{ - struct shell_surface *shsurf = container_of(listener, - struct shell_surface, - surface_destroy_listener); - - if (shsurf->resource) - wl_resource_destroy(shsurf->resource); - else - destroy_shell_surface(shsurf); -} - -static void -shell_surface_configure(struct weston_surface *, int32_t, int32_t, int32_t, int32_t); - -static struct shell_surface * -get_shell_surface(struct weston_surface *surface) -{ - if (surface->configure == shell_surface_configure) - return surface->configure_private; - else - return NULL; -} - -static struct shell_surface * -create_shell_surface(void *shell, struct weston_surface *surface, - const struct weston_shell_client *client) -{ - struct shell_surface *shsurf; - - if (surface->configure) { - weston_log("surface->configure already set\n"); - return NULL; - } - - shsurf = calloc(1, sizeof *shsurf); - if (!shsurf) { - weston_log("no memory to allocate shell surface\n"); - return NULL; - } - - surface->configure = shell_surface_configure; - surface->configure_private = shsurf; - - shsurf->shell = (struct desktop_shell *) shell; - shsurf->unresponsive = 0; - shsurf->saved_position_valid = false; - shsurf->saved_rotation_valid = false; - shsurf->surface = surface; - shsurf->fullscreen.type = WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT; - shsurf->fullscreen.framerate = 0; - shsurf->fullscreen.black_surface = NULL; - shsurf->ping_timer = NULL; - wl_list_init(&shsurf->fullscreen.transform.link); - - wl_signal_init(&shsurf->destroy_signal); - shsurf->surface_destroy_listener.notify = shell_handle_surface_destroy; - wl_signal_add(&surface->destroy_signal, - &shsurf->surface_destroy_listener); - - /* init link so its safe to always remove it in destroy_shell_surface */ - wl_list_init(&shsurf->link); - wl_list_init(&shsurf->popup.grab_link); - - /* empty when not in use */ - wl_list_init(&shsurf->rotation.transform.link); - weston_matrix_init(&shsurf->rotation.rotation); - - wl_list_init(&shsurf->workspace_transform.link); - - shsurf->type = SHELL_SURFACE_NONE; - shsurf->next_type = SHELL_SURFACE_NONE; - - shsurf->client = client; - - return shsurf; -} - -static void -shell_get_shell_surface(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, - struct wl_resource *surface_resource) -{ - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - struct desktop_shell *shell = wl_resource_get_user_data(resource); - struct shell_surface *shsurf; - - if (get_shell_surface(surface)) { - wl_resource_post_error(surface_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "desktop_shell::get_shell_surface already requested"); - return; - } - - shsurf = create_shell_surface(shell, surface, &shell_client); - if (!shsurf) { - wl_resource_post_error(surface_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "surface->configure already set"); - return; - } - - shsurf->resource = - wl_resource_create(client, - &wl_shell_surface_interface, 1, id); - wl_resource_set_implementation(shsurf->resource, - &shell_surface_implementation, - shsurf, shell_destroy_shell_surface); -} - -static const struct wl_shell_interface shell_implementation = { - shell_get_shell_surface -}; - -static void -shell_fade(struct desktop_shell *shell, enum fade_type type); - -static int -screensaver_timeout(void *data) -{ - struct desktop_shell *shell = data; - - shell_fade(shell, FADE_OUT); - - return 1; -} - -static void -handle_screensaver_sigchld(struct weston_process *proc, int status) -{ - struct desktop_shell *shell = - container_of(proc, struct desktop_shell, screensaver.process); - - proc->pid = 0; - - if (shell->locked) - weston_compositor_sleep(shell->compositor); -} - -static void -launch_screensaver(struct desktop_shell *shell) -{ - if (shell->screensaver.binding) - return; - - if (!shell->screensaver.path) { - weston_compositor_sleep(shell->compositor); - return; - } - - if (shell->screensaver.process.pid != 0) { - weston_log("old screensaver still running\n"); - return; - } - - weston_client_launch(shell->compositor, - &shell->screensaver.process, - shell->screensaver.path, - handle_screensaver_sigchld); -} - -static void -terminate_screensaver(struct desktop_shell *shell) -{ - if (shell->screensaver.process.pid == 0) - return; - - kill(shell->screensaver.process.pid, SIGTERM); -} - -static void -configure_static_surface(struct weston_surface *es, struct weston_layer *layer, int32_t width, int32_t height) -{ - struct weston_surface *s, *next; - - if (width == 0) - return; - - wl_list_for_each_safe(s, next, &layer->surface_list, layer_link) { - if (s->output == es->output && s != es) { - weston_surface_unmap(s); - s->configure = NULL; - } - } - - weston_surface_configure(es, es->output->x, es->output->y, width, height); - - if (wl_list_empty(&es->layer_link)) { - wl_list_insert(&layer->surface_list, &es->layer_link); - weston_compositor_schedule_repaint(es->compositor); - } -} - -static void -background_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height) -{ - struct desktop_shell *shell = es->configure_private; - - configure_static_surface(es, &shell->background_layer, width, height); -} - -static void -desktop_shell_set_background(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *output_resource, - struct wl_resource *surface_resource) -{ - struct desktop_shell *shell = wl_resource_get_user_data(resource); - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - - if (surface->configure) { - wl_resource_post_error(surface_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "surface role already assigned"); - return; - } - - surface->configure = background_configure; - surface->configure_private = shell; - surface->output = wl_resource_get_user_data(output_resource); - desktop_shell_send_configure(resource, 0, - surface_resource, - surface->output->width, - surface->output->height); -} - -static void -panel_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height) -{ - struct desktop_shell *shell = es->configure_private; - - configure_static_surface(es, &shell->panel_layer, width, height); -} - -static void -desktop_shell_set_panel(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *output_resource, - struct wl_resource *surface_resource) -{ - struct desktop_shell *shell = wl_resource_get_user_data(resource); - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - - if (surface->configure) { - wl_resource_post_error(surface_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "surface role already assigned"); - return; - } - - surface->configure = panel_configure; - surface->configure_private = shell; - surface->output = wl_resource_get_user_data(output_resource); - desktop_shell_send_configure(resource, 0, - surface_resource, - surface->output->width, - surface->output->height); -} - -static void -lock_surface_configure(struct weston_surface *surface, int32_t sx, int32_t sy, int32_t width, int32_t height) -{ - struct desktop_shell *shell = surface->configure_private; - - if (width == 0) - return; - - surface->geometry.width = width; - surface->geometry.height = height; - center_on_output(surface, get_default_output(shell->compositor)); - - if (!weston_surface_is_mapped(surface)) { - wl_list_insert(&shell->lock_layer.surface_list, - &surface->layer_link); - weston_surface_update_transform(surface); - shell_fade(shell, FADE_IN); - } -} - -static void -handle_lock_surface_destroy(struct wl_listener *listener, void *data) -{ - struct desktop_shell *shell = - container_of(listener, struct desktop_shell, lock_surface_listener); - - weston_log("lock surface gone\n"); - shell->lock_surface = NULL; -} - -static void -desktop_shell_set_lock_surface(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *surface_resource) -{ - struct desktop_shell *shell = wl_resource_get_user_data(resource); - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - - shell->prepare_event_sent = false; - - if (!shell->locked) - return; - - shell->lock_surface = surface; - - shell->lock_surface_listener.notify = handle_lock_surface_destroy; - wl_signal_add(&surface->destroy_signal, - &shell->lock_surface_listener); - - surface->configure = lock_surface_configure; - surface->configure_private = shell; -} - -static void -resume_desktop(struct desktop_shell *shell) -{ - struct workspace *ws = get_current_workspace(shell); - - terminate_screensaver(shell); - - wl_list_remove(&shell->lock_layer.link); - wl_list_insert(&shell->compositor->cursor_layer.link, - &shell->fullscreen_layer.link); - wl_list_insert(&shell->fullscreen_layer.link, - &shell->panel_layer.link); - if (shell->showing_input_panels) { - wl_list_insert(&shell->panel_layer.link, - &shell->input_panel_layer.link); - wl_list_insert(&shell->input_panel_layer.link, - &ws->layer.link); - } else { - wl_list_insert(&shell->panel_layer.link, &ws->layer.link); - } - - restore_focus_state(shell, get_current_workspace(shell)); - - shell->locked = false; - shell_fade(shell, FADE_IN); - weston_compositor_damage_all(shell->compositor); -} - -static void -desktop_shell_unlock(struct wl_client *client, - struct wl_resource *resource) -{ - struct desktop_shell *shell = wl_resource_get_user_data(resource); - - shell->prepare_event_sent = false; - - if (shell->locked) - resume_desktop(shell); -} - -static void -desktop_shell_set_grab_surface(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *surface_resource) -{ - struct desktop_shell *shell = wl_resource_get_user_data(resource); - - shell->grab_surface = wl_resource_get_user_data(surface_resource); -} - -static void -desktop_shell_desktop_ready(struct wl_client *client, - struct wl_resource *resource) -{ - struct desktop_shell *shell = wl_resource_get_user_data(resource); - - shell_fade_startup(shell); -} - -static const struct desktop_shell_interface desktop_shell_implementation = { - desktop_shell_set_background, - desktop_shell_set_panel, - desktop_shell_set_lock_surface, - desktop_shell_unlock, - desktop_shell_set_grab_surface, - desktop_shell_desktop_ready -}; - -static enum shell_surface_type -get_shell_surface_type(struct weston_surface *surface) -{ - struct shell_surface *shsurf; - - shsurf = get_shell_surface(surface); - if (!shsurf) - return SHELL_SURFACE_NONE; - return shsurf->type; -} - -static void -move_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void *data) -{ - struct weston_surface *focus = - (struct weston_surface *) seat->pointer->focus; - struct weston_surface *surface; - struct shell_surface *shsurf; - - surface = weston_surface_get_main_surface(focus); - if (surface == NULL) - return; - - shsurf = get_shell_surface(surface); - if (shsurf == NULL || shsurf->type == SHELL_SURFACE_FULLSCREEN || - shsurf->type == SHELL_SURFACE_MAXIMIZED) - return; - - surface_move(shsurf, (struct weston_seat *) seat); -} - -static void -touch_move_binding(struct weston_seat *seat, uint32_t time, void *data) -{ - struct weston_surface *focus = - (struct weston_surface *) seat->touch->focus; - struct weston_surface *surface; - struct shell_surface *shsurf; - - surface = weston_surface_get_main_surface(focus); - if (surface == NULL) - return; - - shsurf = get_shell_surface(surface); - if (shsurf == NULL || shsurf->type == SHELL_SURFACE_FULLSCREEN || - shsurf->type == SHELL_SURFACE_MAXIMIZED) - return; - - surface_touch_move(shsurf, (struct weston_seat *) seat); -} - -static void -resize_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void *data) -{ - struct weston_surface *focus = - (struct weston_surface *) seat->pointer->focus; - struct weston_surface *surface; - uint32_t edges = 0; - int32_t x, y; - struct shell_surface *shsurf; - - surface = weston_surface_get_main_surface(focus); - if (surface == NULL) - return; - - shsurf = get_shell_surface(surface); - if (!shsurf || shsurf->type == SHELL_SURFACE_FULLSCREEN || - shsurf->type == SHELL_SURFACE_MAXIMIZED) - return; - - weston_surface_from_global(surface, - wl_fixed_to_int(seat->pointer->grab_x), - wl_fixed_to_int(seat->pointer->grab_y), - &x, &y); - - if (x < surface->geometry.width / 3) - edges |= WL_SHELL_SURFACE_RESIZE_LEFT; - else if (x < 2 * surface->geometry.width / 3) - edges |= 0; - else - edges |= WL_SHELL_SURFACE_RESIZE_RIGHT; - - if (y < surface->geometry.height / 3) - edges |= WL_SHELL_SURFACE_RESIZE_TOP; - else if (y < 2 * surface->geometry.height / 3) - edges |= 0; - else - edges |= WL_SHELL_SURFACE_RESIZE_BOTTOM; - - surface_resize(shsurf, (struct weston_seat *) seat, edges); -} - -static void -surface_opacity_binding(struct weston_seat *seat, uint32_t time, uint32_t axis, - wl_fixed_t value, void *data) -{ - float step = 0.005; - struct shell_surface *shsurf; - struct weston_surface *focus = - (struct weston_surface *) seat->pointer->focus; - struct weston_surface *surface; - - /* XXX: broken for windows containing sub-surfaces */ - surface = weston_surface_get_main_surface(focus); - if (surface == NULL) - return; - - shsurf = get_shell_surface(surface); - if (!shsurf) - return; - - surface->alpha -= wl_fixed_to_double(value) * step; - - if (surface->alpha > 1.0) - surface->alpha = 1.0; - if (surface->alpha < step) - surface->alpha = step; - - weston_surface_geometry_dirty(surface); - weston_surface_damage(surface); -} - -static void -do_zoom(struct weston_seat *seat, uint32_t time, uint32_t key, uint32_t axis, - wl_fixed_t value) -{ - struct weston_seat *ws = (struct weston_seat *) seat; - struct weston_compositor *compositor = ws->compositor; - struct weston_output *output; - float increment; - - wl_list_for_each(output, &compositor->output_list, link) { - if (pixman_region32_contains_point(&output->region, - wl_fixed_to_double(seat->pointer->x), - wl_fixed_to_double(seat->pointer->y), - NULL)) { - if (key == KEY_PAGEUP) - increment = output->zoom.increment; - else if (key == KEY_PAGEDOWN) - increment = -output->zoom.increment; - else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) - /* For every pixel zoom 20th of a step */ - increment = output->zoom.increment * - -wl_fixed_to_double(value) / 20.0; - else - increment = 0; - - output->zoom.level += increment; - - if (output->zoom.level < 0.0) - output->zoom.level = 0.0; - else if (output->zoom.level > output->zoom.max_level) - output->zoom.level = output->zoom.max_level; - else if (!output->zoom.active) { - output->zoom.active = 1; - output->disable_planes++; - } - - output->zoom.spring_z.target = output->zoom.level; - - weston_output_update_zoom(output, output->zoom.type); - } - } -} - -static void -zoom_axis_binding(struct weston_seat *seat, uint32_t time, uint32_t axis, - wl_fixed_t value, void *data) -{ - do_zoom(seat, time, 0, axis, value); -} - -static void -zoom_key_binding(struct weston_seat *seat, uint32_t time, uint32_t key, - void *data) -{ - do_zoom(seat, time, key, 0, 0); -} - -static void -terminate_binding(struct weston_seat *seat, uint32_t time, uint32_t key, - void *data) -{ - struct weston_compositor *compositor = data; - - wl_display_terminate(compositor->wl_display); -} - -static void -rotate_grab_motion(struct weston_pointer_grab *grab, uint32_t time) -{ - struct rotate_grab *rotate = - container_of(grab, struct rotate_grab, base.grab); - struct weston_pointer *pointer = grab->pointer; - struct shell_surface *shsurf = rotate->base.shsurf; - struct weston_surface *surface; - float cx, cy, dx, dy, cposx, cposy, dposx, dposy, r; - - if (!shsurf) - return; - - surface = shsurf->surface; - - cx = 0.5f * surface->geometry.width; - cy = 0.5f * surface->geometry.height; - - dx = wl_fixed_to_double(pointer->x) - rotate->center.x; - dy = wl_fixed_to_double(pointer->y) - rotate->center.y; - r = sqrtf(dx * dx + dy * dy); - - wl_list_remove(&shsurf->rotation.transform.link); - weston_surface_geometry_dirty(shsurf->surface); - - if (r > 20.0f) { - struct weston_matrix *matrix = - &shsurf->rotation.transform.matrix; - - weston_matrix_init(&rotate->rotation); - weston_matrix_rotate_xy(&rotate->rotation, dx / r, dy / r); - - weston_matrix_init(matrix); - weston_matrix_translate(matrix, -cx, -cy, 0.0f); - weston_matrix_multiply(matrix, &shsurf->rotation.rotation); - weston_matrix_multiply(matrix, &rotate->rotation); - weston_matrix_translate(matrix, cx, cy, 0.0f); - - wl_list_insert( - &shsurf->surface->geometry.transformation_list, - &shsurf->rotation.transform.link); - } else { - wl_list_init(&shsurf->rotation.transform.link); - weston_matrix_init(&shsurf->rotation.rotation); - weston_matrix_init(&rotate->rotation); - } - - /* We need to adjust the position of the surface - * in case it was resized in a rotated state before */ - cposx = surface->geometry.x + cx; - cposy = surface->geometry.y + cy; - dposx = rotate->center.x - cposx; - dposy = rotate->center.y - cposy; - if (dposx != 0.0f || dposy != 0.0f) { - weston_surface_set_position(surface, - surface->geometry.x + dposx, - surface->geometry.y + dposy); - } - - /* Repaint implies weston_surface_update_transform(), which - * lazily applies the damage due to rotation update. - */ - weston_compositor_schedule_repaint(shsurf->surface->compositor); -} - -static void -rotate_grab_button(struct weston_pointer_grab *grab, - uint32_t time, uint32_t button, uint32_t state_w) -{ - struct rotate_grab *rotate = - container_of(grab, struct rotate_grab, base.grab); - struct weston_pointer *pointer = grab->pointer; - struct shell_surface *shsurf = rotate->base.shsurf; - enum wl_pointer_button_state state = state_w; - - if (pointer->button_count == 0 && - state == WL_POINTER_BUTTON_STATE_RELEASED) { - if (shsurf) - weston_matrix_multiply(&shsurf->rotation.rotation, - &rotate->rotation); - shell_grab_end(&rotate->base); - free(rotate); - } -} - -static void -rotate_grab_cancel(struct weston_pointer_grab *grab) -{ - struct rotate_grab *rotate = - container_of(grab, struct rotate_grab, base.grab); - - shell_grab_end(&rotate->base); - free(rotate); -} - -static const struct weston_pointer_grab_interface rotate_grab_interface = { - noop_grab_focus, - rotate_grab_motion, - rotate_grab_button, - rotate_grab_cancel, -}; - -static void -surface_rotate(struct shell_surface *surface, struct weston_seat *seat) -{ - struct rotate_grab *rotate; - float dx, dy; - float r; - - rotate = malloc(sizeof *rotate); - if (!rotate) - return; - - weston_surface_to_global_float(surface->surface, - surface->surface->geometry.width * 0.5f, - surface->surface->geometry.height * 0.5f, - &rotate->center.x, &rotate->center.y); - - dx = wl_fixed_to_double(seat->pointer->x) - rotate->center.x; - dy = wl_fixed_to_double(seat->pointer->y) - rotate->center.y; - r = sqrtf(dx * dx + dy * dy); - if (r > 20.0f) { - struct weston_matrix inverse; - - weston_matrix_init(&inverse); - weston_matrix_rotate_xy(&inverse, dx / r, -dy / r); - weston_matrix_multiply(&surface->rotation.rotation, &inverse); - - weston_matrix_init(&rotate->rotation); - weston_matrix_rotate_xy(&rotate->rotation, dx / r, dy / r); - } else { - weston_matrix_init(&surface->rotation.rotation); - weston_matrix_init(&rotate->rotation); - } - - shell_grab_start(&rotate->base, &rotate_grab_interface, surface, - seat->pointer, DESKTOP_SHELL_CURSOR_ARROW); -} - -static void -rotate_binding(struct weston_seat *seat, uint32_t time, uint32_t button, - void *data) -{ - struct weston_surface *focus = - (struct weston_surface *) seat->pointer->focus; - struct weston_surface *base_surface; - struct shell_surface *surface; - - base_surface = weston_surface_get_main_surface(focus); - if (base_surface == NULL) - return; - - surface = get_shell_surface(base_surface); - if (!surface || surface->type == SHELL_SURFACE_FULLSCREEN || - surface->type == SHELL_SURFACE_MAXIMIZED) - return; - - surface_rotate(surface, seat); -} - -static void -lower_fullscreen_layer(struct desktop_shell *shell) -{ - struct workspace *ws; - struct weston_surface *surface, *prev; - - ws = get_current_workspace(shell); - wl_list_for_each_reverse_safe(surface, prev, - &shell->fullscreen_layer.surface_list, - layer_link) - weston_surface_restack(surface, &ws->layer.surface_list); -} - -static void -activate(struct desktop_shell *shell, struct weston_surface *es, - struct weston_seat *seat) -{ - struct weston_surface *main_surface; - struct focus_state *state; - struct workspace *ws; - - main_surface = weston_surface_get_main_surface(es); - - weston_surface_activate(es, seat); - - state = ensure_focus_state(shell, seat); - if (state == NULL) - return; - - state->keyboard_focus = es; - wl_list_remove(&state->surface_destroy_listener.link); - wl_signal_add(&es->destroy_signal, &state->surface_destroy_listener); - - switch (get_shell_surface_type(main_surface)) { - case SHELL_SURFACE_FULLSCREEN: - /* should on top of panels */ - shell_stack_fullscreen(get_shell_surface(main_surface)); - shell_configure_fullscreen(get_shell_surface(main_surface)); - break; - default: - restore_all_output_modes(shell->compositor); - ws = get_current_workspace(shell); - weston_surface_restack(main_surface, &ws->layer.surface_list); - break; - } -} - -/* no-op func for checking black surface */ -static void -black_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height) -{ -} - -static bool -is_black_surface (struct weston_surface *es, struct weston_surface **fs_surface) -{ - if (es->configure == black_surface_configure) { - if (fs_surface) - *fs_surface = (struct weston_surface *)es->configure_private; - return true; - } - return false; -} - -static void -activate_binding(struct weston_seat *seat, - struct desktop_shell *shell, - struct weston_surface *focus) -{ - struct weston_surface *main_surface; - - if (!focus) - return; - - if (is_black_surface(focus, &main_surface)) - focus = main_surface; - - main_surface = weston_surface_get_main_surface(focus); - if (get_shell_surface_type(main_surface) == SHELL_SURFACE_NONE) - return; - - activate(shell, focus, seat); -} - -static void -click_to_activate_binding(struct weston_seat *seat, uint32_t time, uint32_t button, - void *data) -{ - if (seat->pointer->grab != &seat->pointer->default_grab) - return; - - activate_binding(seat, data, - (struct weston_surface *) seat->pointer->focus); -} - -static void -touch_to_activate_binding(struct weston_seat *seat, uint32_t time, void *data) -{ - if (seat->touch->grab != &seat->touch->default_grab) - return; - - activate_binding(seat, data, - (struct weston_surface *) seat->touch->focus); -} - -static void -lock(struct desktop_shell *shell) -{ - struct workspace *ws = get_current_workspace(shell); - - if (shell->locked) { - weston_compositor_sleep(shell->compositor); - return; - } - - shell->locked = true; - - /* Hide all surfaces by removing the fullscreen, panel and - * toplevel layers. This way nothing else can show or receive - * input events while we are locked. */ - - wl_list_remove(&shell->panel_layer.link); - wl_list_remove(&shell->fullscreen_layer.link); - if (shell->showing_input_panels) - wl_list_remove(&shell->input_panel_layer.link); - wl_list_remove(&ws->layer.link); - wl_list_insert(&shell->compositor->cursor_layer.link, - &shell->lock_layer.link); - - launch_screensaver(shell); - - /* TODO: disable bindings that should not work while locked. */ - - /* All this must be undone in resume_desktop(). */ -} - -static void -unlock(struct desktop_shell *shell) -{ - if (!shell->locked || shell->lock_surface) { - shell_fade(shell, FADE_IN); - return; - } - - /* If desktop-shell client has gone away, unlock immediately. */ - if (!shell->child.desktop_shell) { - resume_desktop(shell); - return; - } - - if (shell->prepare_event_sent) - return; - - desktop_shell_send_prepare_lock_surface(shell->child.desktop_shell); - shell->prepare_event_sent = true; -} - -static void -shell_fade_done(struct weston_surface_animation *animation, void *data) -{ - struct desktop_shell *shell = data; - - shell->fade.animation = NULL; - - switch (shell->fade.type) { - case FADE_IN: - weston_surface_destroy(shell->fade.surface); - shell->fade.surface = NULL; - break; - case FADE_OUT: - lock(shell); - break; - } -} - -static struct weston_surface * -shell_fade_create_surface(struct desktop_shell *shell) -{ - struct weston_compositor *compositor = shell->compositor; - struct weston_surface *surface; - - surface = weston_surface_create(compositor); - if (!surface) - return NULL; - - weston_surface_configure(surface, 0, 0, 8192, 8192); - weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0); - wl_list_insert(&compositor->fade_layer.surface_list, - &surface->layer_link); - pixman_region32_init(&surface->input); - - return surface; -} - -static void -shell_fade(struct desktop_shell *shell, enum fade_type type) -{ - float tint; - - switch (type) { - case FADE_IN: - tint = 0.0; - break; - case FADE_OUT: - tint = 1.0; - break; - default: - weston_log("shell: invalid fade type\n"); - return; - } - - shell->fade.type = type; - - if (shell->fade.surface == NULL) { - shell->fade.surface = shell_fade_create_surface(shell); - if (!shell->fade.surface) - return; - - shell->fade.surface->alpha = 1.0 - tint; - weston_surface_update_transform(shell->fade.surface); - } - - if (shell->fade.animation) - weston_fade_update(shell->fade.animation, tint); - else - shell->fade.animation = - weston_fade_run(shell->fade.surface, - 1.0 - tint, tint, 300.0, - shell_fade_done, shell); -} - -static void -do_shell_fade_startup(void *data) -{ - struct desktop_shell *shell = data; - - if (shell->startup_animation_type == ANIMATION_FADE) - shell_fade(shell, FADE_IN); - else if (shell->startup_animation_type == ANIMATION_NONE) { - weston_surface_destroy(shell->fade.surface); - shell->fade.surface = NULL; - } -} - -static void -shell_fade_startup(struct desktop_shell *shell) -{ - struct wl_event_loop *loop; - - if (!shell->fade.startup_timer) - return; - - wl_event_source_remove(shell->fade.startup_timer); - shell->fade.startup_timer = NULL; - - loop = wl_display_get_event_loop(shell->compositor->wl_display); - wl_event_loop_add_idle(loop, do_shell_fade_startup, shell); -} - -static int -fade_startup_timeout(void *data) -{ - struct desktop_shell *shell = data; - - shell_fade_startup(shell); - return 0; -} - -static void -shell_fade_init(struct desktop_shell *shell) -{ - /* Make compositor output all black, and wait for the desktop-shell - * client to signal it is ready, then fade in. The timer triggers a - * fade-in, in case the desktop-shell client takes too long. - */ - - struct wl_event_loop *loop; - - if (shell->fade.surface != NULL) { - weston_log("%s: warning: fade surface already exists\n", - __func__); - return; - } - - shell->fade.surface = shell_fade_create_surface(shell); - if (!shell->fade.surface) - return; - - weston_surface_update_transform(shell->fade.surface); - weston_surface_damage(shell->fade.surface); - - loop = wl_display_get_event_loop(shell->compositor->wl_display); - shell->fade.startup_timer = - wl_event_loop_add_timer(loop, fade_startup_timeout, shell); - wl_event_source_timer_update(shell->fade.startup_timer, 15000); -} - -static void -idle_handler(struct wl_listener *listener, void *data) -{ - struct desktop_shell *shell = - container_of(listener, struct desktop_shell, idle_listener); - - shell_fade(shell, FADE_OUT); - /* lock() is called from shell_fade_done() */ -} - -static void -wake_handler(struct wl_listener *listener, void *data) -{ - struct desktop_shell *shell = - container_of(listener, struct desktop_shell, wake_listener); - - unlock(shell); -} - -static void -show_input_panels(struct wl_listener *listener, void *data) -{ - struct desktop_shell *shell = - container_of(listener, struct desktop_shell, - show_input_panel_listener); - struct input_panel_surface *surface, *next; - struct weston_surface *ws; - - shell->text_input.surface = (struct weston_surface*)data; - - if (shell->showing_input_panels) - return; - - shell->showing_input_panels = true; - - if (!shell->locked) - wl_list_insert(&shell->panel_layer.link, - &shell->input_panel_layer.link); - - wl_list_for_each_safe(surface, next, - &shell->input_panel.surfaces, link) { - ws = surface->surface; - if (!ws->buffer_ref.buffer) - continue; - wl_list_insert(&shell->input_panel_layer.surface_list, - &ws->layer_link); - weston_surface_geometry_dirty(ws); - weston_surface_update_transform(ws); - weston_surface_damage(ws); - weston_slide_run(ws, ws->geometry.height, 0, NULL, NULL); - } -} - -static void -hide_input_panels(struct wl_listener *listener, void *data) -{ - struct desktop_shell *shell = - container_of(listener, struct desktop_shell, - hide_input_panel_listener); - struct weston_surface *surface, *next; - - if (!shell->showing_input_panels) - return; - - shell->showing_input_panels = false; - - if (!shell->locked) - wl_list_remove(&shell->input_panel_layer.link); - - wl_list_for_each_safe(surface, next, - &shell->input_panel_layer.surface_list, layer_link) - weston_surface_unmap(surface); -} - -static void -update_input_panels(struct wl_listener *listener, void *data) -{ - struct desktop_shell *shell = - container_of(listener, struct desktop_shell, - update_input_panel_listener); - - memcpy(&shell->text_input.cursor_rectangle, data, sizeof(pixman_box32_t)); -} - -static void -center_on_output(struct weston_surface *surface, struct weston_output *output) -{ - int32_t surf_x, surf_y, width, height; - float x, y; - - surface_subsurfaces_boundingbox(surface, &surf_x, &surf_y, &width, &height); - - x = output->x + (output->width - width) / 2 - surf_x / 2; - y = output->y + (output->height - height) / 2 - surf_y / 2; - - weston_surface_configure(surface, x, y, width, height); -} - -static void -weston_surface_set_initial_position (struct weston_surface *surface, - struct desktop_shell *shell) -{ - struct weston_compositor *compositor = shell->compositor; - int ix = 0, iy = 0; - int range_x, range_y; - int dx, dy, x, y, panel_height; - struct weston_output *output, *target_output = NULL; - struct weston_seat *seat; - - /* As a heuristic place the new window on the same output as the - * pointer. Falling back to the output containing 0, 0. - * - * TODO: Do something clever for touch too? - */ - wl_list_for_each(seat, &compositor->seat_list, link) { - if (seat->pointer) { - ix = wl_fixed_to_int(seat->pointer->x); - iy = wl_fixed_to_int(seat->pointer->y); - break; - } - } - - wl_list_for_each(output, &compositor->output_list, link) { - if (pixman_region32_contains_point(&output->region, ix, iy, NULL)) { - target_output = output; - break; - } - } - - if (!target_output) { - weston_surface_set_position(surface, 10 + random() % 400, - 10 + random() % 400); - return; - } - - /* Valid range within output where the surface will still be onscreen. - * If this is negative it means that the surface is bigger than - * output. - */ - panel_height = get_output_panel_height(shell, target_output); - range_x = target_output->width - surface->geometry.width; - range_y = (target_output->height - panel_height) - - surface->geometry.height; - - if (range_x > 0) - dx = random() % range_x; - else - dx = 0; - - if (range_y > 0) - dy = panel_height + random() % range_y; - else - dy = panel_height; - - x = target_output->x + dx; - y = target_output->y + dy; - - weston_surface_set_position (surface, x, y); -} - -static void -map(struct desktop_shell *shell, struct weston_surface *surface, - int32_t width, int32_t height, int32_t sx, int32_t sy) -{ - struct weston_compositor *compositor = shell->compositor; - struct shell_surface *shsurf = get_shell_surface(surface); - enum shell_surface_type surface_type = shsurf->type; - struct weston_surface *parent; - struct weston_seat *seat; - struct workspace *ws; - int panel_height = 0; - int32_t surf_x, surf_y; - - surface->geometry.width = width; - surface->geometry.height = height; - weston_surface_geometry_dirty(surface); - - /* initial positioning, see also configure() */ - switch (surface_type) { - case SHELL_SURFACE_TOPLEVEL: - weston_surface_set_initial_position(surface, shell); - break; - case SHELL_SURFACE_FULLSCREEN: - center_on_output(surface, shsurf->fullscreen_output); - shell_map_fullscreen(shsurf); - break; - case SHELL_SURFACE_MAXIMIZED: - /* use surface configure to set the geometry */ - panel_height = get_output_panel_height(shell,surface->output); - surface_subsurfaces_boundingbox(shsurf->surface, &surf_x, &surf_y, - NULL, NULL); - weston_surface_set_position(surface, shsurf->output->x - surf_x, - shsurf->output->y + panel_height - surf_y); - break; - case SHELL_SURFACE_POPUP: - shell_map_popup(shsurf); - break; - case SHELL_SURFACE_NONE: - weston_surface_set_position(surface, - surface->geometry.x + sx, - surface->geometry.y + sy); - break; - default: - ; - } - - /* surface stacking order, see also activate() */ - switch (surface_type) { - case SHELL_SURFACE_POPUP: - case SHELL_SURFACE_TRANSIENT: - parent = shsurf->parent; - wl_list_insert(parent->layer_link.prev, &surface->layer_link); - break; - case SHELL_SURFACE_FULLSCREEN: - case SHELL_SURFACE_NONE: - break; - case SHELL_SURFACE_XWAYLAND: - default: - ws = get_current_workspace(shell); - wl_list_insert(&ws->layer.surface_list, &surface->layer_link); - break; - } - - if (surface_type != SHELL_SURFACE_NONE) { - weston_surface_update_transform(surface); - if (surface_type == SHELL_SURFACE_MAXIMIZED) - surface->output = shsurf->output; - } - - switch (surface_type) { - /* XXX: xwayland's using the same fields for transient type */ - case SHELL_SURFACE_XWAYLAND: - case SHELL_SURFACE_TRANSIENT: - if (shsurf->transient.flags == - WL_SHELL_SURFACE_TRANSIENT_INACTIVE) - break; - case SHELL_SURFACE_TOPLEVEL: - case SHELL_SURFACE_FULLSCREEN: - case SHELL_SURFACE_MAXIMIZED: - if (!shell->locked) { - wl_list_for_each(seat, &compositor->seat_list, link) - activate(shell, surface, seat); - } - break; - default: - break; - } - - if (surface_type == SHELL_SURFACE_TOPLEVEL) - { - switch (shell->win_animation_type) { - case ANIMATION_FADE: - weston_fade_run(surface, 0.0, 1.0, 300.0, NULL, NULL); - break; - case ANIMATION_ZOOM: - weston_zoom_run(surface, 0.5, 1.0, NULL, NULL); - break; - default: - break; - } - } -} - -static void -configure(struct desktop_shell *shell, struct weston_surface *surface, - float x, float y, int32_t width, int32_t height) -{ - enum shell_surface_type surface_type = SHELL_SURFACE_NONE; - struct shell_surface *shsurf; - int32_t surf_x, surf_y; - - shsurf = get_shell_surface(surface); - if (shsurf) - surface_type = shsurf->type; - - weston_surface_configure(surface, x, y, width, height); - - switch (surface_type) { - case SHELL_SURFACE_FULLSCREEN: - shell_stack_fullscreen(shsurf); - shell_configure_fullscreen(shsurf); - break; - case SHELL_SURFACE_MAXIMIZED: - /* setting x, y and using configure to change that geometry */ - surface_subsurfaces_boundingbox(shsurf->surface, &surf_x, &surf_y, - NULL, NULL); - surface->geometry.x = surface->output->x - surf_x; - surface->geometry.y = surface->output->y + - get_output_panel_height(shell,surface->output) - surf_y; - break; - case SHELL_SURFACE_TOPLEVEL: - break; - default: - break; - } - - /* XXX: would a fullscreen surface need the same handling? */ - if (surface->output) { - weston_surface_update_transform(surface); - - if (surface_type == SHELL_SURFACE_MAXIMIZED) - surface->output = shsurf->output; - } -} - -static void -shell_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height) -{ - struct shell_surface *shsurf = get_shell_surface(es); - struct desktop_shell *shell = shsurf->shell; - - int type_changed = 0; - - if (!weston_surface_is_mapped(es) && - !wl_list_empty(&shsurf->popup.grab_link)) { - remove_popup_grab(shsurf); - } - - if (width == 0) - return; - - if (shsurf->next_type != SHELL_SURFACE_NONE && - shsurf->type != shsurf->next_type) { - set_surface_type(shsurf); - type_changed = 1; - } - - if (!weston_surface_is_mapped(es)) { - map(shell, es, width, height, sx, sy); - } else if (type_changed || sx != 0 || sy != 0 || - es->geometry.width != width || - es->geometry.height != height) { - float from_x, from_y; - float to_x, to_y; - - weston_surface_to_global_float(es, 0, 0, &from_x, &from_y); - weston_surface_to_global_float(es, sx, sy, &to_x, &to_y); - configure(shell, es, - es->geometry.x + to_x - from_x, - es->geometry.y + to_y - from_y, - width, height); - } -} - -static void launch_desktop_shell_process(void *data); - -static void -desktop_shell_sigchld(struct weston_process *process, int status) -{ - uint32_t time; - struct desktop_shell *shell = - container_of(process, struct desktop_shell, child.process); - - shell->child.process.pid = 0; - shell->child.client = NULL; /* already destroyed by wayland */ - - /* if desktop-shell dies more than 5 times in 30 seconds, give up */ - time = weston_compositor_get_time(); - if (time - shell->child.deathstamp > 30000) { - shell->child.deathstamp = time; - shell->child.deathcount = 0; - } - - shell->child.deathcount++; - if (shell->child.deathcount > 5) { - weston_log("weston-desktop-shell died, giving up.\n"); - return; - } - - weston_log("weston-desktop-shell died, respawning...\n"); - launch_desktop_shell_process(shell); - shell_fade_startup(shell); -} - -static void -launch_desktop_shell_process(void *data) -{ - struct desktop_shell *shell = data; - const char *shell_exe = LIBEXECDIR "/weston-desktop-shell"; - - shell->child.client = weston_client_launch(shell->compositor, - &shell->child.process, - shell_exe, - desktop_shell_sigchld); - - if (!shell->child.client) - weston_log("not able to start %s\n", shell_exe); -} - -static void -bind_shell(struct wl_client *client, void *data, uint32_t version, uint32_t id) -{ - struct desktop_shell *shell = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, &wl_shell_interface, 1, id); - if (resource) - wl_resource_set_implementation(resource, &shell_implementation, - shell, NULL); -} - -static void -unbind_desktop_shell(struct wl_resource *resource) -{ - struct desktop_shell *shell = wl_resource_get_user_data(resource); - - if (shell->locked) - resume_desktop(shell); - - shell->child.desktop_shell = NULL; - shell->prepare_event_sent = false; -} - -static void -bind_desktop_shell(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct desktop_shell *shell = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, &desktop_shell_interface, - MIN(version, 2), id); - - if (client == shell->child.client) { - wl_resource_set_implementation(resource, - &desktop_shell_implementation, - shell, unbind_desktop_shell); - shell->child.desktop_shell = resource; - - if (version < 2) - shell_fade_startup(shell); - - return; - } - - wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, - "permission to bind desktop_shell denied"); - wl_resource_destroy(resource); -} - -static void -screensaver_configure(struct weston_surface *surface, int32_t sx, int32_t sy, int32_t width, int32_t height) -{ - struct desktop_shell *shell = surface->configure_private; - - if (width == 0) - return; - - /* XXX: starting weston-screensaver beforehand does not work */ - if (!shell->locked) - return; - - center_on_output(surface, surface->output); - - if (wl_list_empty(&surface->layer_link)) { - wl_list_insert(shell->lock_layer.surface_list.prev, - &surface->layer_link); - weston_surface_update_transform(surface); - wl_event_source_timer_update(shell->screensaver.timer, - shell->screensaver.duration); - shell_fade(shell, FADE_IN); - } -} - -static void -screensaver_set_surface(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *surface_resource, - struct wl_resource *output_resource) -{ - struct desktop_shell *shell = wl_resource_get_user_data(resource); - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - struct weston_output *output = wl_resource_get_user_data(output_resource); - - surface->configure = screensaver_configure; - surface->configure_private = shell; - surface->output = output; -} - -static const struct screensaver_interface screensaver_implementation = { - screensaver_set_surface -}; - -static void -unbind_screensaver(struct wl_resource *resource) -{ - struct desktop_shell *shell = wl_resource_get_user_data(resource); - - shell->screensaver.binding = NULL; -} - -static void -bind_screensaver(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct desktop_shell *shell = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, &screensaver_interface, 1, id); - - if (shell->screensaver.binding == NULL) { - wl_resource_set_implementation(resource, - &screensaver_implementation, - shell, unbind_screensaver); - shell->screensaver.binding = resource; - return; - } - - wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, - "interface object already bound"); - wl_resource_destroy(resource); -} - -static void -input_panel_configure(struct weston_surface *surface, int32_t sx, int32_t sy, int32_t width, int32_t height) -{ - struct input_panel_surface *ip_surface = surface->configure_private; - struct desktop_shell *shell = ip_surface->shell; - float x, y; - uint32_t show_surface = 0; - - if (width == 0) - return; - - if (!weston_surface_is_mapped(surface)) { - if (!shell->showing_input_panels) - return; - - show_surface = 1; - } - - fprintf(stderr, "%s panel: %d, output: %p\n", __FUNCTION__, ip_surface->panel, ip_surface->output); - - if (ip_surface->panel) { - x = shell->text_input.surface->geometry.x + shell->text_input.cursor_rectangle.x2; - y = shell->text_input.surface->geometry.y + shell->text_input.cursor_rectangle.y2; - } else { - x = ip_surface->output->x + (ip_surface->output->width - width) / 2; - y = ip_surface->output->y + ip_surface->output->height - height; - } - - weston_surface_configure(surface, - x, y, - width, height); - - if (show_surface) { - wl_list_insert(&shell->input_panel_layer.surface_list, - &surface->layer_link); - weston_surface_update_transform(surface); - weston_surface_damage(surface); - weston_slide_run(surface, surface->geometry.height, 0, NULL, NULL); - } -} - -static void -destroy_input_panel_surface(struct input_panel_surface *input_panel_surface) -{ - wl_signal_emit(&input_panel_surface->destroy_signal, input_panel_surface); - - wl_list_remove(&input_panel_surface->surface_destroy_listener.link); - wl_list_remove(&input_panel_surface->link); - - input_panel_surface->surface->configure = NULL; - - free(input_panel_surface); -} - -static struct input_panel_surface * -get_input_panel_surface(struct weston_surface *surface) -{ - if (surface->configure == input_panel_configure) { - return surface->configure_private; - } else { - return NULL; - } -} - -static void -input_panel_handle_surface_destroy(struct wl_listener *listener, void *data) -{ - struct input_panel_surface *ipsurface = container_of(listener, - struct input_panel_surface, - surface_destroy_listener); - - if (ipsurface->resource) { - wl_resource_destroy(ipsurface->resource); - } else { - destroy_input_panel_surface(ipsurface); - } -} - -static struct input_panel_surface * -create_input_panel_surface(struct desktop_shell *shell, - struct weston_surface *surface) -{ - struct input_panel_surface *input_panel_surface; - - input_panel_surface = calloc(1, sizeof *input_panel_surface); - if (!input_panel_surface) - return NULL; - - surface->configure = input_panel_configure; - surface->configure_private = input_panel_surface; - - input_panel_surface->shell = shell; - - input_panel_surface->surface = surface; - - wl_signal_init(&input_panel_surface->destroy_signal); - input_panel_surface->surface_destroy_listener.notify = input_panel_handle_surface_destroy; - wl_signal_add(&surface->destroy_signal, - &input_panel_surface->surface_destroy_listener); - - wl_list_init(&input_panel_surface->link); - - return input_panel_surface; -} - -static void -input_panel_surface_set_toplevel(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *output_resource, - uint32_t position) -{ - struct input_panel_surface *input_panel_surface = - wl_resource_get_user_data(resource); - struct desktop_shell *shell = input_panel_surface->shell; - - wl_list_insert(&shell->input_panel.surfaces, - &input_panel_surface->link); - - input_panel_surface->output = wl_resource_get_user_data(output_resource); - input_panel_surface->panel = 0; -} - -static void -input_panel_surface_set_overlay_panel(struct wl_client *client, - struct wl_resource *resource) -{ - struct input_panel_surface *input_panel_surface = - wl_resource_get_user_data(resource); - struct desktop_shell *shell = input_panel_surface->shell; - - wl_list_insert(&shell->input_panel.surfaces, - &input_panel_surface->link); - - input_panel_surface->panel = 1; -} - -static const struct wl_input_panel_surface_interface input_panel_surface_implementation = { - input_panel_surface_set_toplevel, - input_panel_surface_set_overlay_panel -}; - -static void -destroy_input_panel_surface_resource(struct wl_resource *resource) -{ - struct input_panel_surface *ipsurf = - wl_resource_get_user_data(resource); - - destroy_input_panel_surface(ipsurf); -} - -static void -input_panel_get_input_panel_surface(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, - struct wl_resource *surface_resource) -{ - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - struct desktop_shell *shell = wl_resource_get_user_data(resource); - struct input_panel_surface *ipsurf; - - if (get_input_panel_surface(surface)) { - wl_resource_post_error(surface_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "wl_input_panel::get_input_panel_surface already requested"); - return; - } - - ipsurf = create_input_panel_surface(shell, surface); - if (!ipsurf) { - wl_resource_post_error(surface_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "surface->configure already set"); - return; - } - - ipsurf->resource = - wl_resource_create(client, - &wl_input_panel_surface_interface, 1, id); - wl_resource_set_implementation(ipsurf->resource, - &input_panel_surface_implementation, - ipsurf, - destroy_input_panel_surface_resource); -} - -static const struct wl_input_panel_interface input_panel_implementation = { - input_panel_get_input_panel_surface -}; - -static void -unbind_input_panel(struct wl_resource *resource) -{ - struct desktop_shell *shell = wl_resource_get_user_data(resource); - - shell->input_panel.binding = NULL; -} - -static void -bind_input_panel(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct desktop_shell *shell = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, - &wl_input_panel_interface, 1, id); - - if (shell->input_panel.binding == NULL) { - wl_resource_set_implementation(resource, - &input_panel_implementation, - shell, unbind_input_panel); - shell->input_panel.binding = resource; - return; - } - - wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, - "interface object already bound"); - wl_resource_destroy(resource); -} - -struct switcher { - struct desktop_shell *shell; - struct weston_surface *current; - struct wl_listener listener; - struct weston_keyboard_grab grab; -}; - -static void -switcher_next(struct switcher *switcher) -{ - struct weston_surface *surface; - struct weston_surface *first = NULL, *prev = NULL, *next = NULL; - struct shell_surface *shsurf; - struct workspace *ws = get_current_workspace(switcher->shell); - - wl_list_for_each(surface, &ws->layer.surface_list, layer_link) { - switch (get_shell_surface_type(surface)) { - case SHELL_SURFACE_TOPLEVEL: - case SHELL_SURFACE_FULLSCREEN: - case SHELL_SURFACE_MAXIMIZED: - if (first == NULL) - first = surface; - if (prev == switcher->current) - next = surface; - prev = surface; - surface->alpha = 0.25; - weston_surface_geometry_dirty(surface); - weston_surface_damage(surface); - break; - default: - break; - } - - if (is_black_surface(surface, NULL)) { - surface->alpha = 0.25; - weston_surface_geometry_dirty(surface); - weston_surface_damage(surface); - } - } - - if (next == NULL) - next = first; - - if (next == NULL) - return; - - wl_list_remove(&switcher->listener.link); - wl_signal_add(&next->destroy_signal, &switcher->listener); - - switcher->current = next; - next->alpha = 1.0; - - shsurf = get_shell_surface(switcher->current); - if (shsurf && shsurf->type ==SHELL_SURFACE_FULLSCREEN) - shsurf->fullscreen.black_surface->alpha = 1.0; -} - -static void -switcher_handle_surface_destroy(struct wl_listener *listener, void *data) -{ - struct switcher *switcher = - container_of(listener, struct switcher, listener); - - switcher_next(switcher); -} - -static void -switcher_destroy(struct switcher *switcher) -{ - struct weston_surface *surface; - struct weston_keyboard *keyboard = switcher->grab.keyboard; - struct workspace *ws = get_current_workspace(switcher->shell); - - wl_list_for_each(surface, &ws->layer.surface_list, layer_link) { - surface->alpha = 1.0; - weston_surface_damage(surface); - } - - if (switcher->current) - activate(switcher->shell, switcher->current, - (struct weston_seat *) keyboard->seat); - wl_list_remove(&switcher->listener.link); - weston_keyboard_end_grab(keyboard); - if (keyboard->input_method_resource) - keyboard->grab = &keyboard->input_method_grab; - free(switcher); -} - -static void -switcher_key(struct weston_keyboard_grab *grab, - uint32_t time, uint32_t key, uint32_t state_w) -{ - struct switcher *switcher = container_of(grab, struct switcher, grab); - enum wl_keyboard_key_state state = state_w; - - if (key == KEY_TAB && state == WL_KEYBOARD_KEY_STATE_PRESSED) - switcher_next(switcher); -} - -static void -switcher_modifier(struct weston_keyboard_grab *grab, uint32_t serial, - uint32_t mods_depressed, uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) -{ - struct switcher *switcher = container_of(grab, struct switcher, grab); - struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat; - - if ((seat->modifier_state & switcher->shell->binding_modifier) == 0) - switcher_destroy(switcher); -} - -static void -switcher_cancel(struct weston_keyboard_grab *grab) -{ - struct switcher *switcher = container_of(grab, struct switcher, grab); - - switcher_destroy(switcher); -} - -static const struct weston_keyboard_grab_interface switcher_grab = { - switcher_key, - switcher_modifier, - switcher_cancel, -}; - -static void -switcher_binding(struct weston_seat *seat, uint32_t time, uint32_t key, - void *data) -{ - struct desktop_shell *shell = data; - struct switcher *switcher; - - switcher = malloc(sizeof *switcher); - switcher->shell = shell; - switcher->current = NULL; - switcher->listener.notify = switcher_handle_surface_destroy; - wl_list_init(&switcher->listener.link); - - restore_all_output_modes(shell->compositor); - lower_fullscreen_layer(switcher->shell); - switcher->grab.interface = &switcher_grab; - weston_keyboard_start_grab(seat->keyboard, &switcher->grab); - weston_keyboard_set_focus(seat->keyboard, NULL); - switcher_next(switcher); -} - -static void -backlight_binding(struct weston_seat *seat, uint32_t time, uint32_t key, - void *data) -{ - struct weston_compositor *compositor = data; - struct weston_output *output; - long backlight_new = 0; - - /* TODO: we're limiting to simple use cases, where we assume just - * control on the primary display. We'd have to extend later if we - * ever get support for setting backlights on random desktop LCD - * panels though */ - output = get_default_output(compositor); - if (!output) - return; - - if (!output->set_backlight) - return; - - if (key == KEY_F9 || key == KEY_BRIGHTNESSDOWN) - backlight_new = output->backlight_current - 25; - else if (key == KEY_F10 || key == KEY_BRIGHTNESSUP) - backlight_new = output->backlight_current + 25; - - if (backlight_new < 5) - backlight_new = 5; - if (backlight_new > 255) - backlight_new = 255; - - output->backlight_current = backlight_new; - output->set_backlight(output, output->backlight_current); -} - -struct debug_binding_grab { - struct weston_keyboard_grab grab; - struct weston_seat *seat; - uint32_t key[2]; - int key_released[2]; -}; - -static void -debug_binding_key(struct weston_keyboard_grab *grab, uint32_t time, - uint32_t key, uint32_t state) -{ - struct debug_binding_grab *db = (struct debug_binding_grab *) grab; - struct weston_compositor *ec = db->seat->compositor; - struct wl_display *display = ec->wl_display; - struct wl_resource *resource; - uint32_t serial; - int send = 0, terminate = 0; - int check_binding = 1; - int i; - struct wl_list *resource_list; - - if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { - /* Do not run bindings on key releases */ - check_binding = 0; - - for (i = 0; i < 2; i++) - if (key == db->key[i]) - db->key_released[i] = 1; - - if (db->key_released[0] && db->key_released[1]) { - /* All key releases been swalled so end the grab */ - terminate = 1; - } else if (key != db->key[0] && key != db->key[1]) { - /* Should not swallow release of other keys */ - send = 1; - } - } else if (key == db->key[0] && !db->key_released[0]) { - /* Do not check bindings for the first press of the binding - * key. This allows it to be used as a debug shortcut. - * We still need to swallow this event. */ - check_binding = 0; - } else if (db->key[1]) { - /* If we already ran a binding don't process another one since - * we can't keep track of all the binding keys that were - * pressed in order to swallow the release events. */ - send = 1; - check_binding = 0; - } - - if (check_binding) { - if (weston_compositor_run_debug_binding(ec, db->seat, time, - key, state)) { - /* We ran a binding so swallow the press and keep the - * grab to swallow the released too. */ - send = 0; - terminate = 0; - db->key[1] = key; - } else { - /* Terminate the grab since the key pressed is not a - * debug binding key. */ - send = 1; - terminate = 1; - } - } - - if (send) { - serial = wl_display_next_serial(display); - resource_list = &grab->keyboard->focus_resource_list; - wl_resource_for_each(resource, resource_list) { - wl_keyboard_send_key(resource, serial, time, key, state); - } - } - - if (terminate) { - weston_keyboard_end_grab(grab->keyboard); - if (grab->keyboard->input_method_resource) - grab->keyboard->grab = &grab->keyboard->input_method_grab; - free(db); - } -} - -static void -debug_binding_modifiers(struct weston_keyboard_grab *grab, uint32_t serial, - uint32_t mods_depressed, uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) -{ - struct wl_resource *resource; - struct wl_list *resource_list; - - resource_list = &grab->keyboard->focus_resource_list; - - wl_resource_for_each(resource, resource_list) { - wl_keyboard_send_modifiers(resource, serial, mods_depressed, - mods_latched, mods_locked, group); - } -} - -static void -debug_binding_cancel(struct weston_keyboard_grab *grab) -{ - struct debug_binding_grab *db = (struct debug_binding_grab *) grab; - - weston_keyboard_end_grab(grab->keyboard); - free(db); -} - -struct weston_keyboard_grab_interface debug_binding_keyboard_grab = { - debug_binding_key, - debug_binding_modifiers, - debug_binding_cancel, -}; - -static void -debug_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data) -{ - struct debug_binding_grab *grab; - - grab = calloc(1, sizeof *grab); - if (!grab) - return; - - grab->seat = (struct weston_seat *) seat; - grab->key[0] = key; - grab->grab.interface = &debug_binding_keyboard_grab; - weston_keyboard_start_grab(seat->keyboard, &grab->grab); -} - -static void -force_kill_binding(struct weston_seat *seat, uint32_t time, uint32_t key, - void *data) -{ - struct weston_surface *focus_surface; - struct wl_client *client; - struct desktop_shell *shell = data; - struct weston_compositor *compositor = shell->compositor; - pid_t pid; - - focus_surface = seat->keyboard->focus; - if (!focus_surface) - return; - - wl_signal_emit(&compositor->kill_signal, focus_surface); - - client = wl_resource_get_client(focus_surface->resource); - wl_client_get_credentials(client, &pid, NULL, NULL); - - /* Skip clients that we launched ourselves (the credentials of - * the socketpair is ours) */ - if (pid == getpid()) - return; - - kill(pid, SIGKILL); -} - -static void -workspace_up_binding(struct weston_seat *seat, uint32_t time, - uint32_t key, void *data) -{ - struct desktop_shell *shell = data; - unsigned int new_index = shell->workspaces.current; - - if (shell->locked) - return; - if (new_index != 0) - new_index--; - - change_workspace(shell, new_index); -} - -static void -workspace_down_binding(struct weston_seat *seat, uint32_t time, - uint32_t key, void *data) -{ - struct desktop_shell *shell = data; - unsigned int new_index = shell->workspaces.current; - - if (shell->locked) - return; - if (new_index < shell->workspaces.num - 1) - new_index++; - - change_workspace(shell, new_index); -} - -static void -workspace_f_binding(struct weston_seat *seat, uint32_t time, - uint32_t key, void *data) -{ - struct desktop_shell *shell = data; - unsigned int new_index; - - if (shell->locked) - return; - new_index = key - KEY_F1; - if (new_index >= shell->workspaces.num) - new_index = shell->workspaces.num - 1; - - change_workspace(shell, new_index); -} - -static void -workspace_move_surface_up_binding(struct weston_seat *seat, uint32_t time, - uint32_t key, void *data) -{ - struct desktop_shell *shell = data; - unsigned int new_index = shell->workspaces.current; - - if (shell->locked) - return; - - if (new_index != 0) - new_index--; - - take_surface_to_workspace_by_seat(shell, seat, new_index); -} - -static void -workspace_move_surface_down_binding(struct weston_seat *seat, uint32_t time, - uint32_t key, void *data) -{ - struct desktop_shell *shell = data; - unsigned int new_index = shell->workspaces.current; - - if (shell->locked) - return; - - if (new_index < shell->workspaces.num - 1) - new_index++; - - take_surface_to_workspace_by_seat(shell, seat, new_index); -} - -static void -shell_destroy(struct wl_listener *listener, void *data) -{ - struct desktop_shell *shell = - container_of(listener, struct desktop_shell, destroy_listener); - struct workspace **ws; - - if (shell->child.client) - wl_client_destroy(shell->child.client); - - wl_list_remove(&shell->idle_listener.link); - wl_list_remove(&shell->wake_listener.link); - wl_list_remove(&shell->show_input_panel_listener.link); - wl_list_remove(&shell->hide_input_panel_listener.link); - - wl_array_for_each(ws, &shell->workspaces.array) - workspace_destroy(*ws); - wl_array_release(&shell->workspaces.array); - - free(shell->screensaver.path); - free(shell); -} - -static void -shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) -{ - uint32_t mod; - int i, num_workspace_bindings; - - /* fixed bindings */ - weston_compositor_add_key_binding(ec, KEY_BACKSPACE, - MODIFIER_CTRL | MODIFIER_ALT, - terminate_binding, ec); - weston_compositor_add_button_binding(ec, BTN_LEFT, 0, - click_to_activate_binding, - shell); - weston_compositor_add_touch_binding(ec, 0, - touch_to_activate_binding, - shell); - weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL, - MODIFIER_SUPER | MODIFIER_ALT, - surface_opacity_binding, NULL); - weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL, - MODIFIER_SUPER, zoom_axis_binding, - NULL); - - /* configurable bindings */ - mod = shell->binding_modifier; - weston_compositor_add_key_binding(ec, KEY_PAGEUP, mod, - zoom_key_binding, NULL); - weston_compositor_add_key_binding(ec, KEY_PAGEDOWN, mod, - zoom_key_binding, NULL); - weston_compositor_add_button_binding(ec, BTN_LEFT, mod, move_binding, - shell); - weston_compositor_add_touch_binding(ec, mod, touch_move_binding, shell); - weston_compositor_add_button_binding(ec, BTN_MIDDLE, mod, - resize_binding, shell); - - if (ec->capabilities & WESTON_CAP_ROTATION_ANY) - weston_compositor_add_button_binding(ec, BTN_RIGHT, mod, - rotate_binding, NULL); - - weston_compositor_add_key_binding(ec, KEY_TAB, mod, switcher_binding, - shell); - weston_compositor_add_key_binding(ec, KEY_F9, mod, backlight_binding, - ec); - weston_compositor_add_key_binding(ec, KEY_BRIGHTNESSDOWN, 0, - backlight_binding, ec); - weston_compositor_add_key_binding(ec, KEY_F10, mod, backlight_binding, - ec); - weston_compositor_add_key_binding(ec, KEY_BRIGHTNESSUP, 0, - backlight_binding, ec); - weston_compositor_add_key_binding(ec, KEY_K, mod, - force_kill_binding, shell); - weston_compositor_add_key_binding(ec, KEY_UP, mod, - workspace_up_binding, shell); - weston_compositor_add_key_binding(ec, KEY_DOWN, mod, - workspace_down_binding, shell); - weston_compositor_add_key_binding(ec, KEY_UP, mod | MODIFIER_SHIFT, - workspace_move_surface_up_binding, - shell); - weston_compositor_add_key_binding(ec, KEY_DOWN, mod | MODIFIER_SHIFT, - workspace_move_surface_down_binding, - shell); - - /* Add bindings for mod+F[1-6] for workspace 1 to 6. */ - if (shell->workspaces.num > 1) { - num_workspace_bindings = shell->workspaces.num; - if (num_workspace_bindings > 6) - num_workspace_bindings = 6; - for (i = 0; i < num_workspace_bindings; i++) - weston_compositor_add_key_binding(ec, KEY_F1 + i, mod, - workspace_f_binding, - shell); - } - - /* Debug bindings */ - weston_compositor_add_key_binding(ec, KEY_SPACE, mod | MODIFIER_SHIFT, - debug_binding, shell); -} - -WL_EXPORT int -module_init(struct weston_compositor *ec, - int *argc, char *argv[]) -{ - struct weston_seat *seat; - struct desktop_shell *shell; - struct workspace **pws; - unsigned int i; - struct wl_event_loop *loop; - - shell = zalloc(sizeof *shell); - if (shell == NULL) - return -1; - - shell->compositor = ec; - - shell->destroy_listener.notify = shell_destroy; - wl_signal_add(&ec->destroy_signal, &shell->destroy_listener); - shell->idle_listener.notify = idle_handler; - wl_signal_add(&ec->idle_signal, &shell->idle_listener); - shell->wake_listener.notify = wake_handler; - wl_signal_add(&ec->wake_signal, &shell->wake_listener); - shell->show_input_panel_listener.notify = show_input_panels; - wl_signal_add(&ec->show_input_panel_signal, &shell->show_input_panel_listener); - shell->hide_input_panel_listener.notify = hide_input_panels; - wl_signal_add(&ec->hide_input_panel_signal, &shell->hide_input_panel_listener); - shell->update_input_panel_listener.notify = update_input_panels; - wl_signal_add(&ec->update_input_panel_signal, &shell->update_input_panel_listener); - ec->ping_handler = ping_handler; - ec->shell_interface.shell = shell; - ec->shell_interface.create_shell_surface = create_shell_surface; - ec->shell_interface.set_toplevel = set_toplevel; - ec->shell_interface.set_transient = set_transient; - ec->shell_interface.set_fullscreen = set_fullscreen; - ec->shell_interface.set_xwayland = set_xwayland; - ec->shell_interface.move = surface_move; - ec->shell_interface.resize = surface_resize; - ec->shell_interface.set_title = set_title; - - wl_list_init(&shell->input_panel.surfaces); - - weston_layer_init(&shell->fullscreen_layer, &ec->cursor_layer.link); - weston_layer_init(&shell->panel_layer, &shell->fullscreen_layer.link); - weston_layer_init(&shell->background_layer, &shell->panel_layer.link); - weston_layer_init(&shell->lock_layer, NULL); - weston_layer_init(&shell->input_panel_layer, NULL); - - wl_array_init(&shell->workspaces.array); - wl_list_init(&shell->workspaces.client_list); - - shell_configuration(shell); - - for (i = 0; i < shell->workspaces.num; i++) { - pws = wl_array_add(&shell->workspaces.array, sizeof *pws); - if (pws == NULL) - return -1; - - *pws = workspace_create(); - if (*pws == NULL) - return -1; - } - activate_workspace(shell, 0); - - wl_list_init(&shell->workspaces.anim_sticky_list); - wl_list_init(&shell->workspaces.animation.link); - shell->workspaces.animation.frame = animate_workspace_change_frame; - - if (wl_global_create(ec->wl_display, &wl_shell_interface, 1, - shell, bind_shell) == NULL) - return -1; - - if (wl_global_create(ec->wl_display, - &desktop_shell_interface, 2, - shell, bind_desktop_shell) == NULL) - return -1; - - if (wl_global_create(ec->wl_display, &screensaver_interface, 1, - shell, bind_screensaver) == NULL) - return -1; - - if (wl_global_create(ec->wl_display, &wl_input_panel_interface, 1, - shell, bind_input_panel) == NULL) - return -1; - - if (wl_global_create(ec->wl_display, &workspace_manager_interface, 1, - shell, bind_workspace_manager) == NULL) - return -1; - - shell->child.deathstamp = weston_compositor_get_time(); - - loop = wl_display_get_event_loop(ec->wl_display); - wl_event_loop_add_idle(loop, launch_desktop_shell_process, shell); - - shell->screensaver.timer = - wl_event_loop_add_timer(loop, screensaver_timeout, shell); - - wl_list_for_each(seat, &ec->seat_list, link) - create_pointer_focus_listener(seat); - - shell_add_bindings(ec, shell); - - shell_fade_init(shell); - - return 0; -} diff --git a/src/spring-tool.c b/src/spring-tool.c index 935acc4b..41cc52ce 100644 --- a/src/spring-tool.c +++ b/src/spring-tool.c @@ -25,7 +25,7 @@ #include "compositor.h" WL_EXPORT void -weston_surface_geometry_dirty(struct weston_surface *surface) +weston_view_geometry_dirty(struct weston_view *view) { } @@ -36,7 +36,7 @@ weston_log(const char *fmt, ...) } WL_EXPORT void -weston_compositor_schedule_repaint(struct weston_compositor *compositor) +weston_view_schedule_repaint(struct weston_view *view) { } diff --git a/src/tablet-shell.c b/src/tablet-shell.c deleted file mode 100644 index b055ab23..00000000 --- a/src/tablet-shell.c +++ /dev/null @@ -1,575 +0,0 @@ -/* - * Copyright © 2011 Intel Corporation - * - * Permission to use, copy, modify, distribute, and sell this software and - * its documentation for any purpose is hereby granted without fee, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of the copyright holders not be used in - * advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. The copyright holders make - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include "compositor.h" -#include "tablet-shell-server-protocol.h" - -/* - * TODO: Don't fade back from black until we've received a lockscreen - * attachment. - */ - -enum { - STATE_STARTING, - STATE_LOCKED, - STATE_HOME, - STATE_SWITCHER, - STATE_TASK -}; - -struct tablet_shell { - struct wl_resource *resource; - - struct wl_listener lock_listener; - struct wl_listener unlock_listener; - struct wl_listener destroy_listener; - - struct weston_compositor *compositor; - struct weston_process process; - struct wl_client *client; - - struct weston_surface *surface; - - struct weston_surface *lockscreen_surface; - struct wl_listener lockscreen_listener; - struct weston_layer lockscreen_layer; - - struct weston_layer application_layer; - - struct weston_surface *home_surface; - struct weston_layer homescreen_layer; - - struct weston_surface *switcher_surface; - struct wl_listener switcher_listener; - - struct tablet_client *current_client; - - int state, previous_state; - int long_press_active; - struct wl_event_source *long_press_source; -}; - -struct tablet_client { - struct wl_resource *resource; - struct tablet_shell *shell; - struct wl_client *client; - struct weston_surface *surface; - char *name; -}; - -static void -tablet_shell_destroy(struct wl_listener *listener, void *data); - -static struct tablet_shell * -get_shell(struct weston_compositor *compositor) -{ - struct wl_listener *l; - - l = wl_signal_get(&compositor->destroy_signal, tablet_shell_destroy); - if (l) - return container_of(l, struct tablet_shell, destroy_listener); - - return NULL; -} - -static void -tablet_shell_sigchld(struct weston_process *process, int status) -{ - struct tablet_shell *shell = - container_of(process, struct tablet_shell, process); - - shell->process.pid = 0; - - weston_log("weston-tablet-shell crashed, exit code %d\n", status); -} - -static void -tablet_shell_set_state(struct tablet_shell *shell, int state) -{ - static const char *states[] = { - "STARTING", "LOCKED", "HOME", "SWITCHER", "TASK" - }; - - weston_log("switching to state %s (from %s)\n", - states[state], states[shell->state]); - shell->previous_state = shell->state; - shell->state = state; -} - -static void -tablet_shell_surface_configure(struct weston_surface *surface, - int32_t sx, int32_t sy, int32_t width, int32_t height) -{ - struct tablet_shell *shell = get_shell(surface->compositor); - - if (weston_surface_is_mapped(surface) || width == 0) - return; - - weston_surface_configure(surface, 0, 0, width, height); - - if (surface == shell->lockscreen_surface) { - wl_list_insert(&shell->lockscreen_layer.surface_list, - &surface->layer_link); - } else if (surface == shell->switcher_surface) { - /* */ - } else if (surface == shell->home_surface) { - if (shell->state == STATE_STARTING) { - /* homescreen always visible, at the bottom */ - wl_list_insert(&shell->homescreen_layer.surface_list, - &surface->layer_link); - - tablet_shell_set_state(shell, STATE_LOCKED); - shell->previous_state = STATE_HOME; - tablet_shell_send_show_lockscreen(shell->resource); - } - } else if (shell->current_client && - shell->current_client->surface != surface && - shell->current_client->client == wl_resource_get_client(surface->resource)) { - tablet_shell_set_state(shell, STATE_TASK); - shell->current_client->surface = surface; - weston_zoom_run(surface, 0.3, 1.0, NULL, NULL); - wl_list_insert(&shell->application_layer.surface_list, - &surface->layer_link); - } - - weston_surface_update_transform(surface); -} - -static void -handle_lockscreen_surface_destroy(struct wl_listener *listener, void *data) -{ - struct tablet_shell *shell = - container_of(listener, - struct tablet_shell, lockscreen_listener); - - shell->lockscreen_surface = NULL; - tablet_shell_set_state(shell, shell->previous_state); -} - -static void -tablet_shell_set_lockscreen(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *surface_resource) -{ - struct tablet_shell *shell = wl_resource_get_user_data(resource); - struct weston_surface *es = wl_resource_get_user_data(surface_resource); - - weston_surface_set_position(es, 0, 0); - shell->lockscreen_surface = es; - shell->lockscreen_surface->configure = tablet_shell_surface_configure; - shell->lockscreen_listener.notify = handle_lockscreen_surface_destroy; - wl_signal_add(&es->destroy_signal, &shell->lockscreen_listener); -} - -static void -handle_switcher_surface_destroy(struct wl_listener *listener, void *data) -{ - struct tablet_shell *shell = - container_of(listener, - struct tablet_shell, switcher_listener); - - shell->switcher_surface = NULL; - if (shell->state != STATE_LOCKED) - tablet_shell_set_state(shell, shell->previous_state); -} - -static void -tablet_shell_set_switcher(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *surface_resource) -{ - struct tablet_shell *shell = wl_resource_get_user_data(resource); - struct weston_surface *es = wl_resource_get_user_data(surface_resource); - - /* FIXME: Switcher should be centered and the compositor - * should do the tinting of the background. With the cache - * layer idea, we should be able to hit the framerate on the - * fade/zoom in. */ - shell->switcher_surface = es; - weston_surface_set_position(shell->switcher_surface, 0, 0); - - shell->switcher_listener.notify = handle_switcher_surface_destroy; - wl_signal_add(&es->destroy_signal, &shell->switcher_listener); -} - -static void -tablet_shell_set_homescreen(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *surface_resource) -{ - struct tablet_shell *shell = wl_resource_get_user_data(resource); - - shell->home_surface = wl_resource_get_user_data(surface_resource); - shell->home_surface->configure = tablet_shell_surface_configure; - - weston_surface_set_position(shell->home_surface, 0, 0); -} - -static void -minimize_zoom_done(struct weston_surface_animation *zoom, void *data) -{ - struct tablet_shell *shell = data; - struct weston_compositor *compositor = shell->compositor; - struct weston_seat *seat; - - wl_list_for_each(seat, &compositor->seat_list, link) - weston_surface_activate(shell->home_surface, seat); -} - -static void -tablet_shell_switch_to(struct tablet_shell *shell, - struct weston_surface *surface) -{ - struct weston_compositor *compositor = shell->compositor; - struct weston_seat *seat; - struct weston_surface *current; - - if (shell->state == STATE_SWITCHER) { - wl_list_remove(&shell->switcher_listener.link); - shell->switcher_surface = NULL; - }; - - if (surface == shell->home_surface) { - tablet_shell_set_state(shell, STATE_HOME); - - if (shell->current_client && shell->current_client->surface) { - current = shell->current_client->surface; - weston_zoom_run(current, 1.0, 0.3, - minimize_zoom_done, shell); - } - } else { - fprintf(stderr, "switch to %p\n", surface); - wl_list_for_each(seat, &compositor->seat_list, link) - weston_surface_activate(surface, seat); - tablet_shell_set_state(shell, STATE_TASK); - weston_zoom_run(surface, 0.3, 1.0, NULL, NULL); - } -} - -static void -tablet_shell_show_grid(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *surface_resource) -{ - struct tablet_shell *shell = wl_resource_get_user_data(resource); - struct weston_surface *es = wl_resource_get_user_data(surface_resource); - - tablet_shell_switch_to(shell, es); -} - -static void -tablet_shell_show_panels(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *surface_resource) -{ - struct tablet_shell *shell = wl_resource_get_user_data(resource); - struct weston_surface *es = wl_resource_get_user_data(surface_resource); - - tablet_shell_switch_to(shell, es); -} - -static void -destroy_tablet_client(struct wl_resource *resource) -{ - struct tablet_client *tablet_client = - wl_resource_get_user_data(resource); - - free(tablet_client->name); - free(tablet_client); -} - -static void -tablet_client_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -tablet_client_activate(struct wl_client *client, struct wl_resource *resource) -{ - struct tablet_client *tablet_client = wl_resource_get_user_data(resource); - struct tablet_shell *shell = tablet_client->shell; - - shell->current_client = tablet_client; - if (!tablet_client->surface) - return; - - tablet_shell_switch_to(shell, tablet_client->surface); -} - -static const struct tablet_client_interface tablet_client_implementation = { - tablet_client_destroy, - tablet_client_activate -}; - -static void -tablet_shell_create_client(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, const char *name, int fd) -{ - struct tablet_shell *shell = wl_resource_get_user_data(resource); - struct weston_compositor *compositor = shell->compositor; - struct tablet_client *tablet_client; - - tablet_client = malloc(sizeof *tablet_client); - if (tablet_client == NULL) { - wl_resource_post_no_memory(resource); - return; - } - - tablet_client->client = wl_client_create(compositor->wl_display, fd); - tablet_client->shell = shell; - tablet_client->name = strdup(name); - - tablet_client->resource = - wl_resource_create(client, &tablet_client_interface, 1, id); - wl_resource_set_implementation(tablet_client->resource, - &tablet_client_implementation, - tablet_client, destroy_tablet_client); - - tablet_client->surface = NULL; - shell->current_client = tablet_client; - - weston_log("created client %p, id %d, name %s, fd %d\n", - tablet_client->client, id, name, fd); -} - -static const struct tablet_shell_interface tablet_shell_implementation = { - tablet_shell_set_lockscreen, - tablet_shell_set_switcher, - tablet_shell_set_homescreen, - tablet_shell_show_grid, - tablet_shell_show_panels, - tablet_shell_create_client -}; - -static void -launch_ux_daemon(struct tablet_shell *shell) -{ - const char *shell_exe = LIBEXECDIR "/weston-tablet-shell"; - - shell->client = weston_client_launch(shell->compositor, - &shell->process, - shell_exe, tablet_shell_sigchld); -} - -static void -toggle_switcher(struct tablet_shell *shell) -{ - switch (shell->state) { - case STATE_SWITCHER: - tablet_shell_send_hide_switcher(shell->resource); - break; - default: - tablet_shell_send_show_switcher(shell->resource); - tablet_shell_set_state(shell, STATE_SWITCHER); - break; - } -} - -static void -tablet_shell_lock(struct wl_listener *listener, void *data) -{ - struct tablet_shell *shell = - container_of(listener, struct tablet_shell, lock_listener); - - if (shell->state == STATE_LOCKED) - return; - if (shell->state == STATE_SWITCHER) - tablet_shell_send_hide_switcher(shell->resource); - - tablet_shell_send_show_lockscreen(shell->resource); - tablet_shell_set_state(shell, STATE_LOCKED); -} - -static void -tablet_shell_unlock(struct wl_listener *listener, void *data) -{ - struct tablet_shell *shell = - container_of(listener, struct tablet_shell, unlock_listener); - - tablet_shell_set_state(shell, STATE_HOME); -} - -static void -go_home(struct tablet_shell *shell, struct weston_seat *seat) -{ - if (shell->state == STATE_SWITCHER) - tablet_shell_send_hide_switcher(shell->resource); - - weston_surface_activate(shell->home_surface, seat); - - tablet_shell_set_state(shell, STATE_HOME); -} - -static int -long_press_handler(void *data) -{ - struct tablet_shell *shell = data; - - shell->long_press_active = 0; - toggle_switcher(shell); - - return 1; -} - -static void -menu_key_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data) -{ - struct tablet_shell *shell = data; - - if (shell->state == STATE_LOCKED) - return; - - toggle_switcher(shell); -} - -static void -home_key_binding(struct weston_seat *seat, uint32_t time, uint32_t key, void *data) -{ - struct tablet_shell *shell = data; - - if (shell->state == STATE_LOCKED) - return; - - if (1) { - wl_event_source_timer_update(shell->long_press_source, 500); - shell->long_press_active = 1; - } else if (shell->long_press_active) { - /* This code has never been run ... */ - wl_event_source_timer_update(shell->long_press_source, 0); - shell->long_press_active = 0; - - switch (shell->state) { - case STATE_HOME: - case STATE_SWITCHER: - toggle_switcher(shell); - break; - default: - go_home(shell, (struct weston_seat *) seat); - break; - } - } -} - -static void -destroy_tablet_shell(struct wl_resource *resource) -{ -} - -static void -bind_tablet_shell(struct wl_client *client, void *data, uint32_t version, - uint32_t id) -{ - struct tablet_shell *shell = data; - - if (shell->client != client) - /* Throw an error or just let the client fail when it - * tries to access the object?. */ - return; - - shell->resource = - wl_resource_create(client, &tablet_shell_interface, 1, id); - wl_resource_set_implementation(shell->resource, - &tablet_shell_implementation, - shell, destroy_tablet_shell); -} - -static void -tablet_shell_destroy(struct wl_listener *listener, void *data) -{ - struct tablet_shell *shell = - container_of(listener, struct tablet_shell, destroy_listener); - - if (shell->home_surface) - shell->home_surface->configure = NULL; - - if (shell->lockscreen_surface) - shell->lockscreen_surface->configure = NULL; - - wl_event_source_remove(shell->long_press_source); - free(shell); -} - -WL_EXPORT int -module_init(struct weston_compositor *compositor, - int *argc, char *argv[]) -{ - struct tablet_shell *shell; - struct wl_event_loop *loop; - - shell = zalloc(sizeof *shell); - if (shell == NULL) - return -1; - - shell->compositor = compositor; - - shell->destroy_listener.notify = tablet_shell_destroy; - wl_signal_add(&compositor->destroy_signal, &shell->destroy_listener); - shell->lock_listener.notify = tablet_shell_lock; - wl_signal_add(&compositor->idle_signal, &shell->lock_listener); - shell->unlock_listener.notify = tablet_shell_unlock; - wl_signal_add(&compositor->wake_signal, &shell->unlock_listener); - - /* FIXME: This will make the object available to all clients. */ - wl_global_create(compositor->wl_display, &tablet_shell_interface, 1, - shell, bind_tablet_shell); - - loop = wl_display_get_event_loop(compositor->wl_display); - shell->long_press_source = - wl_event_loop_add_timer(loop, long_press_handler, shell); - - weston_compositor_add_key_binding(compositor, KEY_LEFTMETA, 0, - home_key_binding, shell); - weston_compositor_add_key_binding(compositor, KEY_RIGHTMETA, 0, - home_key_binding, shell); - weston_compositor_add_key_binding(compositor, KEY_LEFTMETA, - MODIFIER_SUPER, home_key_binding, - shell); - weston_compositor_add_key_binding(compositor, KEY_RIGHTMETA, - MODIFIER_SUPER, home_key_binding, - shell); - weston_compositor_add_key_binding(compositor, KEY_COMPOSE, 0, - menu_key_binding, shell); - - weston_layer_init(&shell->homescreen_layer, - &compositor->cursor_layer.link); - weston_layer_init(&shell->application_layer, - &compositor->cursor_layer.link); - weston_layer_init(&shell->lockscreen_layer, - &compositor->cursor_layer.link); - launch_ux_daemon(shell); - - tablet_shell_set_state(shell, STATE_STARTING); - - return 0; -} diff --git a/src/text-backend.c b/src/text-backend.c index 107ccd63..59a8b946 100644 --- a/src/text-backend.c +++ b/src/text-backend.c @@ -598,8 +598,8 @@ input_method_context_grab_keyboard(struct wl_client *client, context->keyboard = cr; wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, - seat->xkb_info->keymap_fd, - seat->xkb_info->keymap_size); + keyboard->xkb_info->keymap_fd, + keyboard->xkb_info->keymap_size); if (keyboard->grab != &keyboard->default_grab) { weston_keyboard_end_grab(keyboard); diff --git a/src/udev-seat.c b/src/udev-seat.c index ffaf08aa..f9723f2f 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -83,11 +83,11 @@ device_added(struct udev_device *udev_device, struct udev_input *input) device = evdev_device_create(&seat->base, devnode, fd); if (device == EVDEV_UNHANDLED_DEVICE) { - close(fd); + weston_launcher_close(c->launcher, fd); weston_log("not using input device '%s'.\n", devnode); return 0; } else if (device == NULL) { - close(fd); + weston_launcher_close(c->launcher, fd); weston_log("failed to create input device '%s'.\n", devnode); return 0; } @@ -123,8 +123,9 @@ device_added(struct udev_device *udev_device, struct udev_input *input) output_name = udev_device_get_property_value(udev_device, "WL_OUTPUT"); if (output_name) { + device->output_name = strdup(output_name); wl_list_for_each(output, &c->output_list, link) - if (strcmp(output->name, output_name) == 0) + if (strcmp(output->name, device->output_name) == 0) device->output = output; } @@ -219,9 +220,11 @@ evdev_udev_handler(int fd, uint32_t mask, void *data) if (!strcmp(device->devnode, devnode)) { weston_log("input device %s, %s removed\n", device->devname, device->devnode); + weston_launcher_close(input->compositor->launcher, + device->fd); evdev_device_destroy(device); - break; - } + break; + } } } @@ -278,8 +281,11 @@ udev_input_remove_devices(struct udev_input *input) struct udev_seat *seat; wl_list_for_each(seat, &input->compositor->seat_list, base.link) { - wl_list_for_each_safe(device, next, &seat->devices_list, link) + wl_list_for_each_safe(device, next, &seat->devices_list, link) { + weston_launcher_close(input->compositor->launcher, + device->fd); evdev_device_destroy(device); + } if (seat->base.keyboard) notify_keyboard_focus_out(&seat->base); @@ -338,6 +344,20 @@ drm_led_update(struct weston_seat *seat_base, enum weston_led leds) evdev_led_update(device, leds); } +static void +notify_output_create(struct wl_listener *listener, void *data) +{ + struct udev_seat *seat = container_of(listener, struct udev_seat, + output_create_listener); + struct evdev_device *device; + struct weston_output *output = data; + + wl_list_for_each(device, &seat->devices_list, link) + if (device->output_name && + strcmp(output->name, device->output_name) == 0) + device->output = output; +} + static struct udev_seat * udev_seat_create(struct weston_compositor *c, const char *seat_name) { @@ -350,6 +370,10 @@ udev_seat_create(struct weston_compositor *c, const char *seat_name) weston_seat_init(&seat->base, c, seat_name); seat->base.led_update = drm_led_update; + seat->output_create_listener.notify = notify_output_create; + wl_signal_add(&c->output_created_signal, + &seat->output_create_listener); + wl_list_init(&seat->devices_list); return seat; } @@ -358,6 +382,7 @@ static void udev_seat_destroy(struct udev_seat *seat) { weston_seat_release(&seat->base); + wl_list_remove(&seat->output_create_listener.link); free(seat); } diff --git a/src/udev-seat.h b/src/udev-seat.h index 4cb6f071..e0d491ab 100644 --- a/src/udev-seat.h +++ b/src/udev-seat.h @@ -32,6 +32,7 @@ struct udev_seat { struct weston_seat base; struct wl_list devices_list; + struct wl_listener output_create_listener; }; struct udev_input { @@ -42,7 +43,6 @@ struct udev_input { int enabled; }; - int udev_input_enable(struct udev_input *input, struct udev *udev); void udev_input_disable(struct udev_input *input); int udev_input_init(struct udev_input *input, diff --git a/src/vertex-clipping.c b/src/vertex-clipping.c index 603ce6f4..db527e14 100644 --- a/src/vertex-clipping.c +++ b/src/vertex-clipping.c @@ -23,18 +23,16 @@ #include #include -#include - #include "vertex-clipping.h" -GLfloat -float_difference(GLfloat a, GLfloat b) +float +float_difference(float a, float b) { /* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */ - static const GLfloat max_diff = 4.0f * FLT_MIN; - static const GLfloat max_rel_diff = 4.0e-5; - GLfloat diff = a - b; - GLfloat adiff = fabsf(diff); + static const float max_diff = 4.0f * FLT_MIN; + static const float max_rel_diff = 4.0e-5; + float diff = a - b; + float adiff = fabsf(diff); if (adiff <= max_diff) return 0.0f; @@ -50,12 +48,12 @@ float_difference(GLfloat a, GLfloat b) /* A line segment (p1x, p1y)-(p2x, p2y) intersects the line x = x_arg. * Compute the y coordinate of the intersection. */ -static GLfloat -clip_intersect_y(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y, - GLfloat x_arg) +static float +clip_intersect_y(float p1x, float p1y, float p2x, float p2y, + float x_arg) { - GLfloat a; - GLfloat diff = float_difference(p1x, p2x); + float a; + float diff = float_difference(p1x, p2x); /* Practically vertical line segment, yet the end points have already * been determined to be on different sides of the line. Therefore @@ -72,12 +70,12 @@ clip_intersect_y(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y, /* A line segment (p1x, p1y)-(p2x, p2y) intersects the line y = y_arg. * Compute the x coordinate of the intersection. */ -static GLfloat -clip_intersect_x(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y, - GLfloat y_arg) +static float +clip_intersect_x(float p1x, float p1y, float p2x, float p2y, + float y_arg) { - GLfloat a; - GLfloat diff = float_difference(p1y, p2y); + float a; + float diff = float_difference(p1y, p2y); /* Practically horizontal line segment, yet the end points have already * been determined to be on different sides of the line. Therefore @@ -99,32 +97,32 @@ enum path_transition { }; static void -clip_append_vertex(struct clip_context *ctx, GLfloat x, GLfloat y) +clip_append_vertex(struct clip_context *ctx, float x, float y) { *ctx->vertices.x++ = x; *ctx->vertices.y++ = y; } static enum path_transition -path_transition_left_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +path_transition_left_edge(struct clip_context *ctx, float x, float y) { return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1); } static enum path_transition -path_transition_right_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +path_transition_right_edge(struct clip_context *ctx, float x, float y) { return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2); } static enum path_transition -path_transition_top_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +path_transition_top_edge(struct clip_context *ctx, float x, float y) { return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1); } static enum path_transition -path_transition_bottom_edge(struct clip_context *ctx, GLfloat x, GLfloat y) +path_transition_bottom_edge(struct clip_context *ctx, float x, float y) { return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2); } @@ -132,9 +130,9 @@ path_transition_bottom_edge(struct clip_context *ctx, GLfloat x, GLfloat y) static void clip_polygon_leftright(struct clip_context *ctx, enum path_transition transition, - GLfloat x, GLfloat y, GLfloat clip_x) + float x, float y, float clip_x) { - GLfloat yi; + float yi; switch (transition) { case PATH_TRANSITION_IN_TO_IN: @@ -163,9 +161,9 @@ clip_polygon_leftright(struct clip_context *ctx, static void clip_polygon_topbottom(struct clip_context *ctx, enum path_transition transition, - GLfloat x, GLfloat y, GLfloat clip_y) + float x, float y, float clip_y) { - GLfloat xi; + float xi; switch (transition) { case PATH_TRANSITION_IN_TO_IN: @@ -193,7 +191,7 @@ clip_polygon_topbottom(struct clip_context *ctx, static void clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src, - GLfloat *dst_x, GLfloat *dst_y) + float *dst_x, float *dst_y) { ctx->prev.x = src->x[src->n - 1]; ctx->prev.y = src->y[src->n - 1]; @@ -203,7 +201,7 @@ clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src, static int clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src, - GLfloat *dst_x, GLfloat *dst_y) + float *dst_x, float *dst_y) { enum path_transition trans; int i; @@ -219,7 +217,7 @@ clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src, static int clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src, - GLfloat *dst_x, GLfloat *dst_y) + float *dst_x, float *dst_y) { enum path_transition trans; int i; @@ -235,7 +233,7 @@ clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src, static int clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src, - GLfloat *dst_x, GLfloat *dst_y) + float *dst_x, float *dst_y) { enum path_transition trans; int i; @@ -251,7 +249,7 @@ clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src, static int clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src, - GLfloat *dst_x, GLfloat *dst_y) + float *dst_x, float *dst_y) { enum path_transition trans; int i; @@ -272,8 +270,8 @@ clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src, int clip_simple(struct clip_context *ctx, struct polygon8 *surf, - GLfloat *ex, - GLfloat *ey) + float *ex, + float *ey) { int i; for (i = 0; i < surf->n; i++) { @@ -286,8 +284,8 @@ clip_simple(struct clip_context *ctx, int clip_transformed(struct clip_context *ctx, struct polygon8 *surf, - GLfloat *ex, - GLfloat *ey) + float *ex, + float *ey) { struct polygon8 polygon; int i, n; diff --git a/src/vertex-clipping.h b/src/vertex-clipping.h index a16b1dfc..f6a98b8d 100644 --- a/src/vertex-clipping.h +++ b/src/vertex-clipping.h @@ -22,44 +22,42 @@ #ifndef _WESTON_VERTEX_CLIPPING_H #define _WESTON_VERTEX_CLIPPING_H -#include - struct polygon8 { - GLfloat x[8]; - GLfloat y[8]; + float x[8]; + float y[8]; int n; }; struct clip_context { struct { - GLfloat x; - GLfloat y; + float x; + float y; } prev; struct { - GLfloat x1, y1; - GLfloat x2, y2; + float x1, y1; + float x2, y2; } clip; struct { - GLfloat *x; - GLfloat *y; + float *x; + float *y; } vertices; }; -GLfloat -float_difference(GLfloat a, GLfloat b); +float +float_difference(float a, float b); int clip_simple(struct clip_context *ctx, struct polygon8 *surf, - GLfloat *ex, - GLfloat *ey); + float *ex, + float *ey); int clip_transformed(struct clip_context *ctx, struct polygon8 *surf, - GLfloat *ex, - GLfloat *ey);\ + float *ex, + float *ey);\ #endif diff --git a/src/weston-launch.c b/src/weston-launch.c index d8364c85..56e22b10 100644 --- a/src/weston-launch.c +++ b/src/weston-launch.c @@ -50,8 +50,6 @@ #include #include -#include - #ifdef HAVE_SYSTEMD_LOGIN #include #endif @@ -70,6 +68,26 @@ #define MAX_ARGV_SIZE 256 +#ifdef HAVE_LIBDRM + +#include + +#else + +static inline int +drmDropMaster(int drm_fd) +{ + return 0; +} + +static inline int +drmSetMaster(int drm_fd) +{ + return 0; +} + +#endif + struct weston_launch { struct pam_conv pc; pam_handle_t *ph; diff --git a/src/xwayland/Makefile.am b/src/xwayland/Makefile.am deleted file mode 100644 index cab59c70..00000000 --- a/src/xwayland/Makefile.am +++ /dev/null @@ -1,40 +0,0 @@ -AM_CPPFLAGS = \ - -I$(top_srcdir)/shared \ - -I$(top_builddir)/src \ - -DDATADIR='"$(datadir)"' \ - -DMODULEDIR='"$(moduledir)"' \ - -DLIBEXECDIR='"$(libexecdir)"' \ - -DXSERVER_PATH='"@XSERVER_PATH@"' - -moduledir = @libdir@/weston -module_LTLIBRARIES = xwayland.la - -xwayland = xwayland.la -xwayland_la_LDFLAGS = -module -avoid-version -xwayland_la_LIBADD = \ - $(XWAYLAND_LIBS) \ - $(top_builddir)/shared/libshared-cairo.la -xwayland_la_CFLAGS = \ - $(GCC_CFLAGS) \ - $(COMPOSITOR_CFLAGS) \ - $(PIXMAN_CFLAGS) \ - $(CAIRO_CFLAGS) -xwayland_la_SOURCES = \ - xwayland.h \ - window-manager.c \ - selection.c \ - dnd.c \ - launcher.c \ - xserver-protocol.c \ - xserver-server-protocol.h \ - hash.c \ - hash.h - -BUILT_SOURCES = \ - xserver-protocol.c \ - xserver-server-protocol.h - -CLEANFILES = $(BUILT_SOURCES) - -wayland_protocoldir = $(top_srcdir)/protocol -include $(top_srcdir)/wayland-scanner.mk diff --git a/src/xwayland/dnd.c b/src/xwayland/dnd.c deleted file mode 100644 index b146e123..00000000 --- a/src/xwayland/dnd.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright © 2013 Intel Corporation - * - * Permission to use, copy, modify, distribute, and sell this software and - * its documentation for any purpose is hereby granted without fee, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of the copyright holders not be used in - * advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. The copyright holders make - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "xwayland.h" - -#include "../../shared/cairo-util.h" -#include "../compositor.h" -#include "xserver-server-protocol.h" -#include "hash.h" - -static void -weston_dnd_start(struct weston_wm *wm, xcb_window_t owner) -{ - uint32_t values[1], version = 4; - - wm->dnd_window = xcb_generate_id(wm->conn); - values[0] = - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | - XCB_EVENT_MASK_PROPERTY_CHANGE; - - xcb_create_window(wm->conn, - XCB_COPY_FROM_PARENT, - wm->dnd_window, - wm->screen->root, - 0, 0, - 8192, 8192, - 0, - XCB_WINDOW_CLASS_INPUT_ONLY, - wm->screen->root_visual, - XCB_CW_EVENT_MASK, values); - xcb_change_property(wm->conn, - XCB_PROP_MODE_REPLACE, - wm->dnd_window, - wm->atom.xdnd_aware, - XCB_ATOM_ATOM, - 32, /* format */ - 1, &version); - - xcb_map_window(wm->conn, wm->dnd_window); - wm->dnd_owner = owner; -} - -static void -weston_dnd_stop(struct weston_wm *wm) -{ - xcb_destroy_window(wm->conn, wm->dnd_window); - wm->dnd_window = XCB_WINDOW_NONE; -} - -struct dnd_data_source { - struct weston_data_source base; - struct weston_wm *wm; - int version; - uint32_t window; -}; - -static void -data_source_accept(struct weston_data_source *base, - uint32_t time, const char *mime_type) -{ - struct dnd_data_source *source = (struct dnd_data_source *) base; - xcb_client_message_event_t client_message; - struct weston_wm *wm = source->wm; - - weston_log("got accept, mime-type %s\n", mime_type); - - /* FIXME: If we rewrote UTF8_STRING to - * text/plain;charset=utf-8 and the source doesn't support the - * mime-type, we'll have to rewrite the mime-type back to - * UTF8_STRING here. */ - - client_message.response_type = XCB_CLIENT_MESSAGE; - client_message.format = 32; - client_message.window = wm->dnd_window; - client_message.type = wm->atom.xdnd_status; - client_message.data.data32[0] = wm->dnd_window; - client_message.data.data32[1] = 2; - if (mime_type) - client_message.data.data32[1] |= 1; - client_message.data.data32[2] = 0; - client_message.data.data32[3] = 0; - client_message.data.data32[4] = wm->atom.xdnd_action_copy; - - xcb_send_event(wm->conn, 0, wm->dnd_owner, - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, - (char *) &client_message); -} - -static void -data_source_send(struct weston_data_source *base, - const char *mime_type, int32_t fd) -{ - struct dnd_data_source *source = (struct dnd_data_source *) base; - struct weston_wm *wm = source->wm; - - weston_log("got send, %s\n", mime_type); - - /* Get data for the utf8_string target */ - xcb_convert_selection(wm->conn, - wm->selection_window, - wm->atom.xdnd_selection, - wm->atom.utf8_string, - wm->atom.wl_selection, - XCB_TIME_CURRENT_TIME); - - xcb_flush(wm->conn); - - fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK); - wm->data_source_fd = fd; -} - -static void -data_source_cancel(struct weston_data_source *source) -{ - weston_log("got cancel\n"); -} - -static void -handle_enter(struct weston_wm *wm, xcb_client_message_event_t *client_message) -{ - struct dnd_data_source *source; - struct weston_seat *seat = weston_wm_pick_seat(wm); - char **p; - const char *name; - uint32_t *types; - int i, length, has_text; - xcb_get_property_cookie_t cookie; - xcb_get_property_reply_t *reply; - - source = malloc(sizeof *source); - if (source == NULL) - return; - - wl_signal_init(&source->base.destroy_signal); - source->base.accept = data_source_accept; - source->base.send = data_source_send; - source->base.cancel = data_source_cancel; - source->wm = wm; - source->window = client_message->data.data32[0]; - source->version = client_message->data.data32[1] >> 24; - - if (client_message->data.data32[1] & 1) { - cookie = xcb_get_property(wm->conn, - 0, /* delete */ - source->window, - wm->atom.xdnd_type_list, - XCB_ATOM_ANY, 0, 2048); - reply = xcb_get_property_reply(wm->conn, cookie, NULL); - types = xcb_get_property_value(reply); - length = reply->value_len; - } else { - reply = NULL; - types = &client_message->data.data32[2]; - length = 3; - } - - wl_array_init(&source->base.mime_types); - has_text = 0; - for (i = 0; i < length; i++) { - if (types[i] == XCB_ATOM_NONE) - continue; - - name = get_atom_name(wm->conn, types[i]); - if (types[i] == wm->atom.utf8_string || - types[i] == wm->atom.text_plain_utf8 || - types[i] == wm->atom.text_plain) { - if (has_text) - continue; - - has_text = 1; - p = wl_array_add(&source->base.mime_types, sizeof *p); - if (p) - *p = strdup("text/plain;charset=utf-8"); - } else if (strchr(name, '/')) { - p = wl_array_add(&source->base.mime_types, sizeof *p); - if (p) - *p = strdup(name); - } - } - - free(reply); - weston_seat_start_drag(seat, &source->base, NULL, NULL); -} - -int -weston_wm_handle_dnd_event(struct weston_wm *wm, - xcb_generic_event_t *event) -{ - xcb_xfixes_selection_notify_event_t *xfixes_selection_notify = - (xcb_xfixes_selection_notify_event_t *) event; - xcb_client_message_event_t *client_message = - (xcb_client_message_event_t *) event; - - switch (event->response_type - wm->xfixes->first_event) { - case XCB_XFIXES_SELECTION_NOTIFY: - if (xfixes_selection_notify->selection != wm->atom.xdnd_selection) - return 0; - - weston_log("XdndSelection owner: %d!\n", - xfixes_selection_notify->owner); - - if (xfixes_selection_notify->owner != XCB_WINDOW_NONE) - weston_dnd_start(wm, xfixes_selection_notify->owner); - else - weston_dnd_stop(wm); - - return 1; - } - - switch (EVENT_TYPE(event)) { - case XCB_CLIENT_MESSAGE: - if (client_message->type == wm->atom.xdnd_enter) { - handle_enter(wm, client_message); - return 1; - } else if (client_message->type == wm->atom.xdnd_leave) { - weston_log("got leave!\n"); - return 1; - } else if (client_message->type == wm->atom.xdnd_drop) { - weston_log("got drop!\n"); - return 1; - } else if (client_message->type == wm->atom.xdnd_drop) { - weston_log("got enter!\n"); - return 1; - } - return 0; - } - - return 0; -} - -void -weston_wm_dnd_init(struct weston_wm *wm) -{ - uint32_t mask; - - mask = - XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | - XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | - XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; - xcb_xfixes_select_selection_input(wm->conn, wm->selection_window, - wm->atom.xdnd_selection, mask); -} diff --git a/src/xwayland/hash.c b/src/xwayland/hash.c deleted file mode 100644 index 54f3de93..00000000 --- a/src/xwayland/hash.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright © 2009 Intel Corporation - * Copyright © 1988-2004 Keith Packard and Bart Massey. - * - * 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 (including the next - * paragraph) 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. - * - * Except as contained in this notice, the names of the authors - * or their institutions shall not be used in advertising or - * otherwise to promote the sale, use or other dealings in this - * Software without prior written authorization from the - * authors. - * - * Authors: - * Eric Anholt - * Keith Packard - */ - -#include - -#include -#include - -#include "hash.h" - -struct hash_entry { - uint32_t hash; - void *data; -}; - -struct hash_table { - struct hash_entry *table; - uint32_t size; - uint32_t rehash; - uint32_t max_entries; - uint32_t size_index; - uint32_t entries; - uint32_t deleted_entries; -}; - -#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) - -/* - * From Knuth -- a good choice for hash/rehash values is p, p-2 where - * p and p-2 are both prime. These tables are sized to have an extra 10% - * free to avoid exponential performance degradation as the hash table fills - */ - -static const uint32_t deleted_data; - -static const struct { - uint32_t max_entries, size, rehash; -} hash_sizes[] = { - { 2, 5, 3 }, - { 4, 7, 5 }, - { 8, 13, 11 }, - { 16, 19, 17 }, - { 32, 43, 41 }, - { 64, 73, 71 }, - { 128, 151, 149 }, - { 256, 283, 281 }, - { 512, 571, 569 }, - { 1024, 1153, 1151 }, - { 2048, 2269, 2267 }, - { 4096, 4519, 4517 }, - { 8192, 9013, 9011 }, - { 16384, 18043, 18041 }, - { 32768, 36109, 36107 }, - { 65536, 72091, 72089 }, - { 131072, 144409, 144407 }, - { 262144, 288361, 288359 }, - { 524288, 576883, 576881 }, - { 1048576, 1153459, 1153457 }, - { 2097152, 2307163, 2307161 }, - { 4194304, 4613893, 4613891 }, - { 8388608, 9227641, 9227639 }, - { 16777216, 18455029, 18455027 }, - { 33554432, 36911011, 36911009 }, - { 67108864, 73819861, 73819859 }, - { 134217728, 147639589, 147639587 }, - { 268435456, 295279081, 295279079 }, - { 536870912, 590559793, 590559791 }, - { 1073741824, 1181116273, 1181116271}, - { 2147483648ul, 2362232233ul, 2362232231ul} -}; - -static int -entry_is_free(struct hash_entry *entry) -{ - return entry->data == NULL; -} - -static int -entry_is_deleted(struct hash_entry *entry) -{ - return entry->data == &deleted_data; -} - -static int -entry_is_present(struct hash_entry *entry) -{ - return entry->data != NULL && entry->data != &deleted_data; -} - -struct hash_table * -hash_table_create(void) -{ - struct hash_table *ht; - - ht = malloc(sizeof(*ht)); - if (ht == NULL) - return NULL; - - ht->size_index = 0; - ht->size = hash_sizes[ht->size_index].size; - ht->rehash = hash_sizes[ht->size_index].rehash; - ht->max_entries = hash_sizes[ht->size_index].max_entries; - ht->table = calloc(ht->size, sizeof(*ht->table)); - ht->entries = 0; - ht->deleted_entries = 0; - - if (ht->table == NULL) { - free(ht); - return NULL; - } - - return ht; -} - -/** - * Frees the given hash table. - */ -void -hash_table_destroy(struct hash_table *ht) -{ - if (!ht) - return; - - free(ht->table); - free(ht); -} - -/** - * Finds a hash table entry with the given key and hash of that key. - * - * Returns NULL if no entry is found. Note that the data pointer may be - * modified by the user. - */ -static void * -hash_table_search(struct hash_table *ht, uint32_t hash) -{ - uint32_t hash_address; - - hash_address = hash % ht->size; - do { - uint32_t double_hash; - - struct hash_entry *entry = ht->table + hash_address; - - if (entry_is_free(entry)) { - return NULL; - } else if (entry_is_present(entry) && entry->hash == hash) { - return entry; - } - - double_hash = 1 + hash % ht->rehash; - - hash_address = (hash_address + double_hash) % ht->size; - } while (hash_address != hash % ht->size); - - return NULL; -} - -void -hash_table_for_each(struct hash_table *ht, - hash_table_iterator_func_t func, void *data) -{ - struct hash_entry *entry; - uint32_t i; - - for (i = 0; i < ht->size; i++) { - entry = ht->table + i; - if (entry_is_present(entry)) - func(entry->data, data); - } -} - -void * -hash_table_lookup(struct hash_table *ht, uint32_t hash) -{ - struct hash_entry *entry; - - entry = hash_table_search(ht, hash); - if (entry != NULL) - return entry->data; - - return NULL; -} - -static void -hash_table_rehash(struct hash_table *ht, unsigned int new_size_index) -{ - struct hash_table old_ht; - struct hash_entry *table, *entry; - - if (new_size_index >= ARRAY_SIZE(hash_sizes)) - return; - - table = calloc(hash_sizes[new_size_index].size, sizeof(*ht->table)); - if (table == NULL) - return; - - old_ht = *ht; - - ht->table = table; - ht->size_index = new_size_index; - ht->size = hash_sizes[ht->size_index].size; - ht->rehash = hash_sizes[ht->size_index].rehash; - ht->max_entries = hash_sizes[ht->size_index].max_entries; - ht->entries = 0; - ht->deleted_entries = 0; - - for (entry = old_ht.table; - entry != old_ht.table + old_ht.size; - entry++) { - if (entry_is_present(entry)) { - hash_table_insert(ht, entry->hash, entry->data); - } - } - - free(old_ht.table); -} - -/** - * Inserts the data with the given hash into the table. - * - * Note that insertion may rearrange the table on a resize or rehash, - * so previously found hash_entries are no longer valid after this function. - */ -int -hash_table_insert(struct hash_table *ht, uint32_t hash, void *data) -{ - uint32_t hash_address; - - if (ht->entries >= ht->max_entries) { - hash_table_rehash(ht, ht->size_index + 1); - } else if (ht->deleted_entries + ht->entries >= ht->max_entries) { - hash_table_rehash(ht, ht->size_index); - } - - hash_address = hash % ht->size; - do { - struct hash_entry *entry = ht->table + hash_address; - uint32_t double_hash; - - if (!entry_is_present(entry)) { - if (entry_is_deleted(entry)) - ht->deleted_entries--; - entry->hash = hash; - entry->data = data; - ht->entries++; - return 0; - } - - double_hash = 1 + hash % ht->rehash; - - hash_address = (hash_address + double_hash) % ht->size; - } while (hash_address != hash % ht->size); - - /* We could hit here if a required resize failed. An unchecked-malloc - * application could ignore this result. - */ - return -1; -} - -/** - * This function deletes the given hash table entry. - * - * Note that deletion doesn't otherwise modify the table, so an iteration over - * the table deleting entries is safe. - */ -void -hash_table_remove(struct hash_table *ht, uint32_t hash) -{ - struct hash_entry *entry; - - entry = hash_table_search(ht, hash); - if (entry != NULL) { - entry->data = (void *) &deleted_data; - ht->entries--; - ht->deleted_entries++; - } -} diff --git a/src/xwayland/hash.h b/src/xwayland/hash.h deleted file mode 100644 index 6e1674e1..00000000 --- a/src/xwayland/hash.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2009 Intel Corporation - * Copyright © 1988-2004 Keith Packard and Bart Massey. - * - * 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 (including the next - * paragraph) 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. - * - * Except as contained in this notice, the names of the authors - * or their institutions shall not be used in advertising or - * otherwise to promote the sale, use or other dealings in this - * Software without prior written authorization from the - * authors. - * - * Authors: - * Eric Anholt - * Keith Packard - */ - -#ifndef HASH_H -#define HASH_H - -struct hash_table; -struct hash_table *hash_table_create(void); -typedef void (*hash_table_iterator_func_t)(void *element, void *data); - -void hash_table_destroy(struct hash_table *ht); -void *hash_table_lookup(struct hash_table *ht, uint32_t hash); -int hash_table_insert(struct hash_table *ht, uint32_t hash, void *data); -void hash_table_remove(struct hash_table *ht, uint32_t hash); -void hash_table_for_each(struct hash_table *ht, - hash_table_iterator_func_t func, void *data); - -#endif diff --git a/src/xwayland/launcher.c b/src/xwayland/launcher.c deleted file mode 100644 index 8d8e060a..00000000 --- a/src/xwayland/launcher.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright © 2011 Intel Corporation - * - * Permission to use, copy, modify, distribute, and sell this software and - * its documentation for any purpose is hereby granted without fee, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of the copyright holders not be used in - * advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. The copyright holders make - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "xwayland.h" -#include "xserver-server-protocol.h" - - -static int -weston_xserver_handle_event(int listen_fd, uint32_t mask, void *data) -{ - struct weston_xserver *wxs = data; - char display[8], s[8]; - int sv[2], client_fd; - char *xserver = NULL; - struct weston_config_section *section; - - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { - weston_log("socketpair failed\n"); - return 1; - } - - wxs->process.pid = fork(); - switch (wxs->process.pid) { - case 0: - /* SOCK_CLOEXEC closes both ends, so we need to unset - * the flag on the client fd. */ - client_fd = dup(sv[1]); - if (client_fd < 0) - return 1; - - snprintf(s, sizeof s, "%d", client_fd); - setenv("WAYLAND_SOCKET", s, 1); - - snprintf(display, sizeof display, ":%d", wxs->display); - - section = weston_config_get_section(wxs->compositor->config, "xwayland", NULL, NULL); - weston_config_section_get_string(section, "path", &xserver, XSERVER_PATH); - - if (execl(xserver, - xserver, - display, - "-wayland", - "-rootless", - "-retro", - "-nolisten", "all", - "-terminate", - NULL) < 0) - weston_log("exec failed: %m\n"); - free(xserver); - _exit(EXIT_FAILURE); - - default: - weston_log("forked X server, pid %d\n", wxs->process.pid); - - close(sv[1]); - wxs->client = wl_client_create(wxs->wl_display, sv[0]); - - weston_watch_process(&wxs->process); - - wl_event_source_remove(wxs->abstract_source); - wl_event_source_remove(wxs->unix_source); - break; - - case -1: - weston_log( "failed to fork\n"); - break; - } - - return 1; -} - -static void -weston_xserver_shutdown(struct weston_xserver *wxs) -{ - char path[256]; - - snprintf(path, sizeof path, "/tmp/.X%d-lock", wxs->display); - unlink(path); - snprintf(path, sizeof path, "/tmp/.X11-unix/X%d", wxs->display); - unlink(path); - if (wxs->process.pid == 0) { - wl_event_source_remove(wxs->abstract_source); - wl_event_source_remove(wxs->unix_source); - } - close(wxs->abstract_fd); - close(wxs->unix_fd); - if (wxs->wm) - weston_wm_destroy(wxs->wm); - wxs->loop = NULL; -} - -static void -weston_xserver_cleanup(struct weston_process *process, int status) -{ - struct weston_xserver *wxs = - container_of(process, struct weston_xserver, process); - - wxs->process.pid = 0; - wxs->client = NULL; - wxs->resource = NULL; - - wxs->abstract_source = - wl_event_loop_add_fd(wxs->loop, wxs->abstract_fd, - WL_EVENT_READABLE, - weston_xserver_handle_event, wxs); - - wxs->unix_source = - wl_event_loop_add_fd(wxs->loop, wxs->unix_fd, - WL_EVENT_READABLE, - weston_xserver_handle_event, wxs); - - if (wxs->wm) { - weston_log("xserver exited, code %d\n", status); - weston_wm_destroy(wxs->wm); - wxs->wm = NULL; - } else { - /* If the X server crashes before it binds to the - * xserver interface, shut down and don't try - * again. */ - weston_log("xserver crashing too fast: %d\n", status); - weston_xserver_shutdown(wxs); - } -} - -static void -bind_xserver(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct weston_xserver *wxs = data; - - /* If it's a different client than the xserver we launched, - * don't start the wm. */ - if (client != wxs->client) - return; - - wxs->resource = - wl_resource_create(client, &xserver_interface, - 1, id); - wl_resource_set_implementation(wxs->resource, &xserver_implementation, - wxs, NULL); - - wxs->wm = weston_wm_create(wxs); - if (wxs->wm == NULL) { - weston_log("failed to create wm\n"); - } - - xserver_send_listen_socket(wxs->resource, wxs->abstract_fd); - xserver_send_listen_socket(wxs->resource, wxs->unix_fd); -} - -static int -bind_to_abstract_socket(int display) -{ - struct sockaddr_un addr; - socklen_t size, name_size; - int fd; - - fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (fd < 0) - return -1; - - addr.sun_family = AF_LOCAL; - name_size = snprintf(addr.sun_path, sizeof addr.sun_path, - "%c/tmp/.X11-unix/X%d", 0, display); - size = offsetof(struct sockaddr_un, sun_path) + name_size; - if (bind(fd, (struct sockaddr *) &addr, size) < 0) { - weston_log("failed to bind to @%s: %s\n", - addr.sun_path + 1, strerror(errno)); - close(fd); - return -1; - } - - if (listen(fd, 1) < 0) { - close(fd); - return -1; - } - - return fd; -} - -static int -bind_to_unix_socket(int display) -{ - struct sockaddr_un addr; - socklen_t size, name_size; - int fd; - - fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (fd < 0) - return -1; - - addr.sun_family = AF_LOCAL; - name_size = snprintf(addr.sun_path, sizeof addr.sun_path, - "/tmp/.X11-unix/X%d", display) + 1; - size = offsetof(struct sockaddr_un, sun_path) + name_size; - unlink(addr.sun_path); - if (bind(fd, (struct sockaddr *) &addr, size) < 0) { - weston_log("failed to bind to %s (%s)\n", - addr.sun_path, strerror(errno)); - close(fd); - return -1; - } - - if (listen(fd, 1) < 0) { - unlink(addr.sun_path); - close(fd); - return -1; - } - - return fd; -} - -static int -create_lockfile(int display, char *lockfile, size_t lsize) -{ - char pid[16], *end; - int fd, size; - pid_t other; - - snprintf(lockfile, lsize, "/tmp/.X%d-lock", display); - fd = open(lockfile, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444); - if (fd < 0 && errno == EEXIST) { - fd = open(lockfile, O_CLOEXEC, O_RDONLY); - if (fd < 0 || read(fd, pid, 11) != 11) { - weston_log("can't read lock file %s: %s\n", - lockfile, strerror(errno)); - if (fd >= 0) - close (fd); - - errno = EEXIST; - return -1; - } - - other = strtol(pid, &end, 0); - if (end != pid + 10) { - weston_log("can't parse lock file %s\n", - lockfile); - close(fd); - errno = EEXIST; - return -1; - } - - if (kill(other, 0) < 0 && errno == ESRCH) { - /* stale lock file; unlink and try again */ - weston_log("unlinking stale lock file %s\n", lockfile); - close(fd); - if (unlink(lockfile)) - /* If we fail to unlink, return EEXIST - so we try the next display number.*/ - errno = EEXIST; - else - errno = EAGAIN; - return -1; - } - - close(fd); - errno = EEXIST; - return -1; - } else if (fd < 0) { - weston_log("failed to create lock file %s: %s\n", - lockfile, strerror(errno)); - return -1; - } - - /* Subtle detail: we use the pid of the wayland - * compositor, not the xserver in the lock file. */ - size = snprintf(pid, sizeof pid, "%10d\n", getpid()); - if (write(fd, pid, size) != size) { - unlink(lockfile); - close(fd); - return -1; - } - - close(fd); - - return 0; -} - -static void -weston_xserver_destroy(struct wl_listener *l, void *data) -{ - struct weston_xserver *wxs = - container_of(l, struct weston_xserver, destroy_listener); - - if (!wxs) - return; - - if (wxs->loop) - weston_xserver_shutdown(wxs); - - free(wxs); -} - -WL_EXPORT int -module_init(struct weston_compositor *compositor, - int *argc, char *argv[]) - -{ - struct wl_display *display = compositor->wl_display; - struct weston_xserver *wxs; - char lockfile[256], display_name[8]; - - wxs = zalloc(sizeof *wxs); - wxs->process.cleanup = weston_xserver_cleanup; - wxs->wl_display = display; - wxs->compositor = compositor; - - wxs->display = 0; - - retry: - if (create_lockfile(wxs->display, lockfile, sizeof lockfile) < 0) { - if (errno == EAGAIN) { - goto retry; - } else if (errno == EEXIST) { - wxs->display++; - goto retry; - } else { - free(wxs); - return -1; - } - } - - wxs->abstract_fd = bind_to_abstract_socket(wxs->display); - if (wxs->abstract_fd < 0 && errno == EADDRINUSE) { - wxs->display++; - unlink(lockfile); - goto retry; - } - - wxs->unix_fd = bind_to_unix_socket(wxs->display); - if (wxs->unix_fd < 0) { - unlink(lockfile); - close(wxs->abstract_fd); - free(wxs); - return -1; - } - - snprintf(display_name, sizeof display_name, ":%d", wxs->display); - weston_log("xserver listening on display %s\n", display_name); - setenv("DISPLAY", display_name, 1); - - wxs->loop = wl_display_get_event_loop(display); - wxs->abstract_source = - wl_event_loop_add_fd(wxs->loop, wxs->abstract_fd, - WL_EVENT_READABLE, - weston_xserver_handle_event, wxs); - wxs->unix_source = - wl_event_loop_add_fd(wxs->loop, wxs->unix_fd, - WL_EVENT_READABLE, - weston_xserver_handle_event, wxs); - - wl_global_create(display, &xserver_interface, 1, wxs, bind_xserver); - - wxs->destroy_listener.notify = weston_xserver_destroy; - wl_signal_add(&compositor->destroy_signal, &wxs->destroy_listener); - - return 0; -} diff --git a/src/xwayland/selection.c b/src/xwayland/selection.c deleted file mode 100644 index b694477e..00000000 --- a/src/xwayland/selection.c +++ /dev/null @@ -1,712 +0,0 @@ -/* - * Copyright © 2012 Intel Corporation - * - * Permission to use, copy, modify, distribute, and sell this software and - * its documentation for any purpose is hereby granted without fee, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of the copyright holders not be used in - * advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. The copyright holders make - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include "xwayland.h" - -static int -writable_callback(int fd, uint32_t mask, void *data) -{ - struct weston_wm *wm = data; - unsigned char *property; - int len, remainder; - - property = xcb_get_property_value(wm->property_reply); - remainder = xcb_get_property_value_length(wm->property_reply) - - wm->property_start; - - len = write(fd, property + wm->property_start, remainder); - if (len == -1) { - free(wm->property_reply); - wm->property_reply = NULL; - if (wm->property_source) - wl_event_source_remove(wm->property_source); - close(fd); - weston_log("write error to target fd: %m\n"); - return 1; - } - - weston_log("wrote %d (chunk size %d) of %d bytes\n", - wm->property_start + len, - len, xcb_get_property_value_length(wm->property_reply)); - - wm->property_start += len; - if (len == remainder) { - free(wm->property_reply); - wm->property_reply = NULL; - if (wm->property_source) - wl_event_source_remove(wm->property_source); - - if (wm->incr) { - xcb_delete_property(wm->conn, - wm->selection_window, - wm->atom.wl_selection); - } else { - weston_log("transfer complete\n"); - close(fd); - } - } - - return 1; -} - -static void -weston_wm_write_property(struct weston_wm *wm, xcb_get_property_reply_t *reply) -{ - wm->property_start = 0; - wm->property_reply = reply; - writable_callback(wm->data_source_fd, WL_EVENT_WRITABLE, wm); - - if (wm->property_reply) - wm->property_source = - wl_event_loop_add_fd(wm->server->loop, - wm->data_source_fd, - WL_EVENT_WRITABLE, - writable_callback, wm); -} - -static void -weston_wm_get_incr_chunk(struct weston_wm *wm) -{ - xcb_get_property_cookie_t cookie; - xcb_get_property_reply_t *reply; - - cookie = xcb_get_property(wm->conn, - 0, /* delete */ - wm->selection_window, - wm->atom.wl_selection, - XCB_GET_PROPERTY_TYPE_ANY, - 0, /* offset */ - 0x1fffffff /* length */); - - reply = xcb_get_property_reply(wm->conn, cookie, NULL); - - dump_property(wm, wm->atom.wl_selection, reply); - - if (xcb_get_property_value_length(reply) > 0) { - weston_wm_write_property(wm, reply); - } else { - weston_log("transfer complete\n"); - close(wm->data_source_fd); - free(reply); - } -} - -struct x11_data_source { - struct weston_data_source base; - struct weston_wm *wm; -}; - -static void -data_source_accept(struct weston_data_source *source, - uint32_t time, const char *mime_type) -{ -} - -static void -data_source_send(struct weston_data_source *base, - const char *mime_type, int32_t fd) -{ - struct x11_data_source *source = (struct x11_data_source *) base; - struct weston_wm *wm = source->wm; - - if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) { - /* Get data for the utf8_string target */ - xcb_convert_selection(wm->conn, - wm->selection_window, - wm->atom.clipboard, - wm->atom.utf8_string, - wm->atom.wl_selection, - XCB_TIME_CURRENT_TIME); - - xcb_flush(wm->conn); - - fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK); - wm->data_source_fd = fd; - } -} - -static void -data_source_cancel(struct weston_data_source *source) -{ -} - -static void -weston_wm_get_selection_targets(struct weston_wm *wm) -{ - struct x11_data_source *source; - struct weston_compositor *compositor; - struct weston_seat *seat = weston_wm_pick_seat(wm); - xcb_get_property_cookie_t cookie; - xcb_get_property_reply_t *reply; - xcb_atom_t *value; - char **p; - uint32_t i; - - cookie = xcb_get_property(wm->conn, - 1, /* delete */ - wm->selection_window, - wm->atom.wl_selection, - XCB_GET_PROPERTY_TYPE_ANY, - 0, /* offset */ - 4096 /* length */); - - reply = xcb_get_property_reply(wm->conn, cookie, NULL); - - dump_property(wm, wm->atom.wl_selection, reply); - - if (reply->type != XCB_ATOM_ATOM) { - free(reply); - return; - } - - source = malloc(sizeof *source); - if (source == NULL) - return; - - wl_signal_init(&source->base.destroy_signal); - source->base.accept = data_source_accept; - source->base.send = data_source_send; - source->base.cancel = data_source_cancel; - source->wm = wm; - - wl_array_init(&source->base.mime_types); - value = xcb_get_property_value(reply); - for (i = 0; i < reply->value_len; i++) { - if (value[i] == wm->atom.utf8_string) { - p = wl_array_add(&source->base.mime_types, sizeof *p); - if (p) - *p = strdup("text/plain;charset=utf-8"); - } - } - - compositor = wm->server->compositor; - weston_seat_set_selection(seat, &source->base, - wl_display_next_serial(compositor->wl_display)); - - free(reply); -} - -static void -weston_wm_get_selection_data(struct weston_wm *wm) -{ - xcb_get_property_cookie_t cookie; - xcb_get_property_reply_t *reply; - - cookie = xcb_get_property(wm->conn, - 1, /* delete */ - wm->selection_window, - wm->atom.wl_selection, - XCB_GET_PROPERTY_TYPE_ANY, - 0, /* offset */ - 0x1fffffff /* length */); - - reply = xcb_get_property_reply(wm->conn, cookie, NULL); - - if (reply->type == wm->atom.incr) { - dump_property(wm, wm->atom.wl_selection, reply); - wm->incr = 1; - free(reply); - } else { - dump_property(wm, wm->atom.wl_selection, reply); - wm->incr = 0; - weston_wm_write_property(wm, reply); - } -} - -static void -weston_wm_handle_selection_notify(struct weston_wm *wm, - xcb_generic_event_t *event) -{ - xcb_selection_notify_event_t *selection_notify = - (xcb_selection_notify_event_t *) event; - - if (selection_notify->property == XCB_ATOM_NONE) { - /* convert selection failed */ - } else if (selection_notify->target == wm->atom.targets) { - weston_wm_get_selection_targets(wm); - } else { - weston_wm_get_selection_data(wm); - } -} - -static const size_t incr_chunk_size = 64 * 1024; - -static void -weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property) -{ - xcb_selection_notify_event_t selection_notify; - - memset(&selection_notify, 0, sizeof selection_notify); - selection_notify.response_type = XCB_SELECTION_NOTIFY; - selection_notify.sequence = 0; - selection_notify.time = wm->selection_request.time; - selection_notify.requestor = wm->selection_request.requestor; - selection_notify.selection = wm->selection_request.selection; - selection_notify.target = wm->selection_request.target; - selection_notify.property = property; - - xcb_send_event(wm->conn, 0, /* propagate */ - wm->selection_request.requestor, - XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify); -} - -static void -weston_wm_send_targets(struct weston_wm *wm) -{ - xcb_atom_t targets[] = { - wm->atom.timestamp, - wm->atom.targets, - wm->atom.utf8_string, - /* wm->atom.compound_text, */ - wm->atom.text, - /* wm->atom.string */ - }; - - xcb_change_property(wm->conn, - XCB_PROP_MODE_REPLACE, - wm->selection_request.requestor, - wm->selection_request.property, - XCB_ATOM_ATOM, - 32, /* format */ - ARRAY_LENGTH(targets), targets); - - weston_wm_send_selection_notify(wm, wm->selection_request.property); -} - -static void -weston_wm_send_timestamp(struct weston_wm *wm) -{ - xcb_change_property(wm->conn, - XCB_PROP_MODE_REPLACE, - wm->selection_request.requestor, - wm->selection_request.property, - XCB_ATOM_INTEGER, - 32, /* format */ - 1, &wm->selection_timestamp); - - weston_wm_send_selection_notify(wm, wm->selection_request.property); -} - -static int -weston_wm_flush_source_data(struct weston_wm *wm) -{ - int length; - - xcb_change_property(wm->conn, - XCB_PROP_MODE_REPLACE, - wm->selection_request.requestor, - wm->selection_request.property, - wm->selection_target, - 8, /* format */ - wm->source_data.size, - wm->source_data.data); - wm->selection_property_set = 1; - length = wm->source_data.size; - wm->source_data.size = 0; - - return length; -} - -static int -weston_wm_read_data_source(int fd, uint32_t mask, void *data) -{ - struct weston_wm *wm = data; - int len, current, available; - void *p; - - current = wm->source_data.size; - if (wm->source_data.size < incr_chunk_size) - p = wl_array_add(&wm->source_data, incr_chunk_size); - else - p = (char *) wm->source_data.data + wm->source_data.size; - available = wm->source_data.alloc - current; - - len = read(fd, p, available); - if (len == -1) { - weston_log("read error from data source: %m\n"); - weston_wm_send_selection_notify(wm, XCB_ATOM_NONE); - wl_event_source_remove(wm->property_source); - close(fd); - wl_array_release(&wm->source_data); - } - - weston_log("read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n", - len, available, mask, len, (char *) p); - - wm->source_data.size = current + len; - if (wm->source_data.size >= incr_chunk_size) { - if (!wm->incr) { - weston_log("got %zu bytes, starting incr\n", - wm->source_data.size); - wm->incr = 1; - xcb_change_property(wm->conn, - XCB_PROP_MODE_REPLACE, - wm->selection_request.requestor, - wm->selection_request.property, - wm->atom.incr, - 32, /* format */ - 1, &incr_chunk_size); - wm->selection_property_set = 1; - wm->flush_property_on_delete = 1; - wl_event_source_remove(wm->property_source); - weston_wm_send_selection_notify(wm, wm->selection_request.property); - } else if (wm->selection_property_set) { - weston_log("got %zu bytes, waiting for " - "property delete\n", wm->source_data.size); - - wm->flush_property_on_delete = 1; - wl_event_source_remove(wm->property_source); - } else { - weston_log("got %zu bytes, " - "property deleted, seting new property\n", - wm->source_data.size); - weston_wm_flush_source_data(wm); - } - } else if (len == 0 && !wm->incr) { - weston_log("non-incr transfer complete\n"); - /* Non-incr transfer all done. */ - weston_wm_flush_source_data(wm); - weston_wm_send_selection_notify(wm, wm->selection_request.property); - xcb_flush(wm->conn); - wl_event_source_remove(wm->property_source); - close(fd); - wl_array_release(&wm->source_data); - wm->selection_request.requestor = XCB_NONE; - } else if (len == 0 && wm->incr) { - weston_log("incr transfer complete\n"); - - wm->flush_property_on_delete = 1; - if (wm->selection_property_set) { - weston_log("got %zu bytes, waiting for " - "property delete\n", wm->source_data.size); - } else { - weston_log("got %zu bytes, " - "property deleted, seting new property\n", - wm->source_data.size); - weston_wm_flush_source_data(wm); - } - xcb_flush(wm->conn); - wl_event_source_remove(wm->property_source); - close(wm->data_source_fd); - wm->data_source_fd = -1; - close(fd); - } else { - weston_log("nothing happened, buffered the bytes\n"); - } - - return 1; -} - -static void -weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type) -{ - struct weston_data_source *source; - struct weston_seat *seat = weston_wm_pick_seat(wm); - int p[2]; - - if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) { - weston_log("pipe2 failed: %m\n"); - weston_wm_send_selection_notify(wm, XCB_ATOM_NONE); - return; - } - - wl_array_init(&wm->source_data); - wm->selection_target = target; - wm->data_source_fd = p[0]; - wm->property_source = wl_event_loop_add_fd(wm->server->loop, - wm->data_source_fd, - WL_EVENT_READABLE, - weston_wm_read_data_source, - wm); - - source = seat->selection_data_source; - source->send(source, mime_type, p[1]); - close(p[1]); -} - -static void -weston_wm_send_incr_chunk(struct weston_wm *wm) -{ - int length; - - weston_log("property deleted\n"); - - wm->selection_property_set = 0; - if (wm->flush_property_on_delete) { - weston_log("setting new property, %zu bytes\n", - wm->source_data.size); - wm->flush_property_on_delete = 0; - length = weston_wm_flush_source_data(wm); - - if (wm->data_source_fd >= 0) { - wm->property_source = - wl_event_loop_add_fd(wm->server->loop, - wm->data_source_fd, - WL_EVENT_READABLE, - weston_wm_read_data_source, - wm); - } else if (length > 0) { - /* Transfer is all done, but queue a flush for - * the delete of the last chunk so we can set - * the 0 sized propert to signal the end of - * the transfer. */ - wm->flush_property_on_delete = 1; - wl_array_release(&wm->source_data); - } else { - wm->selection_request.requestor = XCB_NONE; - } - } -} - -static int -weston_wm_handle_selection_property_notify(struct weston_wm *wm, - xcb_generic_event_t *event) -{ - xcb_property_notify_event_t *property_notify = - (xcb_property_notify_event_t *) event; - - if (property_notify->window == wm->selection_window) { - if (property_notify->state == XCB_PROPERTY_NEW_VALUE && - property_notify->atom == wm->atom.wl_selection && - wm->incr) - weston_wm_get_incr_chunk(wm); - return 1; - } else if (property_notify->window == wm->selection_request.requestor) { - if (property_notify->state == XCB_PROPERTY_DELETE && - property_notify->atom == wm->selection_request.property && - wm->incr) - weston_wm_send_incr_chunk(wm); - return 1; - } - - return 0; -} - -static void -weston_wm_handle_selection_request(struct weston_wm *wm, - xcb_generic_event_t *event) -{ - xcb_selection_request_event_t *selection_request = - (xcb_selection_request_event_t *) event; - - weston_log("selection request, %s, ", - get_atom_name(wm->conn, selection_request->selection)); - weston_log_continue("target %s, ", - get_atom_name(wm->conn, selection_request->target)); - weston_log_continue("property %s\n", - get_atom_name(wm->conn, selection_request->property)); - - wm->selection_request = *selection_request; - wm->incr = 0; - wm->flush_property_on_delete = 0; - - if (selection_request->selection == wm->atom.clipboard_manager) { - /* The weston clipboard should already have grabbed - * the first target, so just send selection notify - * now. This isn't synchronized with the clipboard - * finishing getting the data, so there's a race here. */ - weston_wm_send_selection_notify(wm, wm->selection_request.property); - return; - } - - if (selection_request->target == wm->atom.targets) { - weston_wm_send_targets(wm); - } else if (selection_request->target == wm->atom.timestamp) { - weston_wm_send_timestamp(wm); - } else if (selection_request->target == wm->atom.utf8_string || - selection_request->target == wm->atom.text) { - weston_wm_send_data(wm, wm->atom.utf8_string, - "text/plain;charset=utf-8"); - } else { - weston_log("can only handle UTF8_STRING targets...\n"); - weston_wm_send_selection_notify(wm, XCB_ATOM_NONE); - } -} - -static int -weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm, - xcb_generic_event_t *event) -{ - xcb_xfixes_selection_notify_event_t *xfixes_selection_notify = - (xcb_xfixes_selection_notify_event_t *) event; - struct weston_compositor *compositor; - struct weston_seat *seat = weston_wm_pick_seat(wm); - uint32_t serial; - - if (xfixes_selection_notify->selection != wm->atom.clipboard) - return 0; - - weston_log("xfixes selection notify event: owner %d\n", - xfixes_selection_notify->owner); - - if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) { - if (wm->selection_owner != wm->selection_window) { - /* A real X client selection went away, not our - * proxy selection. Clear the wayland selection. */ - compositor = wm->server->compositor; - serial = wl_display_next_serial(compositor->wl_display); - weston_seat_set_selection(seat, NULL, serial); - } - - wm->selection_owner = XCB_WINDOW_NONE; - - return 1; - } - - wm->selection_owner = xfixes_selection_notify->owner; - - /* We have to use XCB_TIME_CURRENT_TIME when we claim the - * selection, so grab the actual timestamp here so we can - * answer TIMESTAMP conversion requests correctly. */ - if (xfixes_selection_notify->owner == wm->selection_window) { - wm->selection_timestamp = xfixes_selection_notify->timestamp; - weston_log("our window, skipping\n"); - return 1; - } - - wm->incr = 0; - xcb_convert_selection(wm->conn, wm->selection_window, - wm->atom.clipboard, - wm->atom.targets, - wm->atom.wl_selection, - xfixes_selection_notify->timestamp); - - xcb_flush(wm->conn); - - return 1; -} - -int -weston_wm_handle_selection_event(struct weston_wm *wm, - xcb_generic_event_t *event) -{ - switch (event->response_type & ~0x80) { - case XCB_SELECTION_NOTIFY: - weston_wm_handle_selection_notify(wm, event); - return 1; - case XCB_PROPERTY_NOTIFY: - return weston_wm_handle_selection_property_notify(wm, event); - case XCB_SELECTION_REQUEST: - weston_wm_handle_selection_request(wm, event); - return 1; - } - - switch (event->response_type - wm->xfixes->first_event) { - case XCB_XFIXES_SELECTION_NOTIFY: - return weston_wm_handle_xfixes_selection_notify(wm, event); - } - - return 0; -} - -static void -weston_wm_set_selection(struct wl_listener *listener, void *data) -{ - struct weston_seat *seat = data; - struct weston_wm *wm = - container_of(listener, struct weston_wm, selection_listener); - struct weston_data_source *source = seat->selection_data_source; - const char **p, **end; - int has_text_plain = 0; - - if (source == NULL) { - if (wm->selection_owner == wm->selection_window) - xcb_set_selection_owner(wm->conn, - XCB_ATOM_NONE, - wm->atom.clipboard, - wm->selection_timestamp); - return; - } - - if (source->send == data_source_send) - return; - - p = source->mime_types.data; - end = (const char **) - ((char *) source->mime_types.data + source->mime_types.size); - while (p < end) { - weston_log(" %s\n", *p); - if (strcmp(*p, "text/plain") == 0 || - strcmp(*p, "text/plain;charset=utf-8") == 0) - has_text_plain = 1; - p++; - } - - if (has_text_plain) { - xcb_set_selection_owner(wm->conn, - wm->selection_window, - wm->atom.clipboard, - XCB_TIME_CURRENT_TIME); - } else { - xcb_set_selection_owner(wm->conn, - XCB_ATOM_NONE, - wm->atom.clipboard, - XCB_TIME_CURRENT_TIME); - } -} - -void -weston_wm_selection_init(struct weston_wm *wm) -{ - struct weston_seat *seat; - uint32_t values[1], mask; - - wm->selection_request.requestor = XCB_NONE; - - values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE; - wm->selection_window = xcb_generate_id(wm->conn); - xcb_create_window(wm->conn, - XCB_COPY_FROM_PARENT, - wm->selection_window, - wm->screen->root, - 0, 0, - 10, 10, - 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - wm->screen->root_visual, - XCB_CW_EVENT_MASK, values); - - xcb_set_selection_owner(wm->conn, - wm->selection_window, - wm->atom.clipboard_manager, - XCB_TIME_CURRENT_TIME); - - mask = - XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | - XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | - XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; - xcb_xfixes_select_selection_input(wm->conn, wm->selection_window, - wm->atom.clipboard, mask); - - seat = weston_wm_pick_seat(wm); - wm->selection_listener.notify = weston_wm_set_selection; - wl_signal_add(&seat->selection_signal, &wm->selection_listener); - - weston_wm_set_selection(&wm->selection_listener, seat); -} diff --git a/src/xwayland/window-manager.c b/src/xwayland/window-manager.c deleted file mode 100644 index f1b58c9b..00000000 --- a/src/xwayland/window-manager.c +++ /dev/null @@ -1,2186 +0,0 @@ -/* - * Copyright © 2011 Intel Corporation - * - * Permission to use, copy, modify, distribute, and sell this software and - * its documentation for any purpose is hereby granted without fee, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of the copyright holders not be used in - * advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. The copyright holders make - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "xwayland.h" - -#include "../../shared/cairo-util.h" -#include "../compositor.h" -#include "xserver-server-protocol.h" -#include "hash.h" - -struct wm_size_hints { - uint32_t flags; - int32_t x, y; - int32_t width, height; /* should set so old wm's don't mess up */ - int32_t min_width, min_height; - int32_t max_width, max_height; - int32_t width_inc, height_inc; - struct { - int32_t x; - int32_t y; - } min_aspect, max_aspect; - int32_t base_width, base_height; - int32_t win_gravity; -}; - -#define USPosition (1L << 0) -#define USSize (1L << 1) -#define PPosition (1L << 2) -#define PSize (1L << 3) -#define PMinSize (1L << 4) -#define PMaxSize (1L << 5) -#define PResizeInc (1L << 6) -#define PAspect (1L << 7) -#define PBaseSize (1L << 8) -#define PWinGravity (1L << 9) - -struct motif_wm_hints { - uint32_t flags; - uint32_t functions; - uint32_t decorations; - int32_t input_mode; - uint32_t status; -}; - -#define MWM_HINTS_FUNCTIONS (1L << 0) -#define MWM_HINTS_DECORATIONS (1L << 1) -#define MWM_HINTS_INPUT_MODE (1L << 2) -#define MWM_HINTS_STATUS (1L << 3) - -#define MWM_FUNC_ALL (1L << 0) -#define MWM_FUNC_RESIZE (1L << 1) -#define MWM_FUNC_MOVE (1L << 2) -#define MWM_FUNC_MINIMIZE (1L << 3) -#define MWM_FUNC_MAXIMIZE (1L << 4) -#define MWM_FUNC_CLOSE (1L << 5) - -#define MWM_DECOR_ALL (1L << 0) -#define MWM_DECOR_BORDER (1L << 1) -#define MWM_DECOR_RESIZEH (1L << 2) -#define MWM_DECOR_TITLE (1L << 3) -#define MWM_DECOR_MENU (1L << 4) -#define MWM_DECOR_MINIMIZE (1L << 5) -#define MWM_DECOR_MAXIMIZE (1L << 6) - -#define MWM_INPUT_MODELESS 0 -#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1 -#define MWM_INPUT_SYSTEM_MODAL 2 -#define MWM_INPUT_FULL_APPLICATION_MODAL 3 -#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL - -#define MWM_TEAROFF_WINDOW (1L<<0) - -#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 -#define _NET_WM_MOVERESIZE_SIZE_TOP 1 -#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 -#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 -#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 -#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 -#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 -#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 -#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */ -#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */ -#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ -#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ - -struct weston_wm_window { - struct weston_wm *wm; - xcb_window_t id; - xcb_window_t frame_id; - cairo_surface_t *cairo_surface; - struct weston_surface *surface; - struct shell_surface *shsurf; - struct wl_listener surface_destroy_listener; - struct wl_event_source *repaint_source; - struct wl_event_source *configure_source; - int properties_dirty; - int pid; - char *machine; - char *class; - char *name; - struct weston_wm_window *transient_for; - uint32_t protocols; - xcb_atom_t type; - int width, height; - int x, y; - int saved_width, saved_height; - int decorate; - int override_redirect; - int fullscreen; - int has_alpha; - struct wm_size_hints size_hints; - struct motif_wm_hints motif_hints; -}; - -static struct weston_wm_window * -get_wm_window(struct weston_surface *surface); - -static void -weston_wm_window_schedule_repaint(struct weston_wm_window *window); - -static int __attribute__ ((format (printf, 1, 2))) -wm_log(const char *fmt, ...) -{ -#ifdef WM_DEBUG - int l; - va_list argp; - - va_start(argp, fmt); - l = weston_vlog(fmt, argp); - va_end(argp); - - return l; -#else - return 0; -#endif -} - -static int __attribute__ ((format (printf, 1, 2))) -wm_log_continue(const char *fmt, ...) -{ -#ifdef WM_DEBUG - int l; - va_list argp; - - va_start(argp, fmt); - l = weston_vlog_continue(fmt, argp); - va_end(argp); - - return l; -#else - return 0; -#endif -} - - -const char * -get_atom_name(xcb_connection_t *c, xcb_atom_t atom) -{ - xcb_get_atom_name_cookie_t cookie; - xcb_get_atom_name_reply_t *reply; - xcb_generic_error_t *e; - static char buffer[64]; - - if (atom == XCB_ATOM_NONE) - return "None"; - - cookie = xcb_get_atom_name (c, atom); - reply = xcb_get_atom_name_reply (c, cookie, &e); - - if(reply) { - snprintf(buffer, sizeof buffer, "%.*s", - xcb_get_atom_name_name_length (reply), - xcb_get_atom_name_name (reply)); - } else { - snprintf(buffer, sizeof buffer, "(atom %u)", atom); - } - - free(reply); - - return buffer; -} - -static xcb_cursor_t -xcb_cursor_image_load_cursor(struct weston_wm *wm, const XcursorImage *img) -{ - xcb_connection_t *c = wm->conn; - xcb_screen_iterator_t s = xcb_setup_roots_iterator(xcb_get_setup(c)); - xcb_screen_t *screen = s.data; - xcb_gcontext_t gc; - xcb_pixmap_t pix; - xcb_render_picture_t pic; - xcb_cursor_t cursor; - int stride = img->width * 4; - - pix = xcb_generate_id(c); - xcb_create_pixmap(c, 32, pix, screen->root, img->width, img->height); - - pic = xcb_generate_id(c); - xcb_render_create_picture(c, pic, pix, wm->format_rgba.id, 0, 0); - - gc = xcb_generate_id(c); - xcb_create_gc(c, gc, pix, 0, 0); - - xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, - img->width, img->height, 0, 0, 0, 32, - stride * img->height, (uint8_t *) img->pixels); - xcb_free_gc(c, gc); - - cursor = xcb_generate_id(c); - xcb_render_create_cursor(c, cursor, pic, img->xhot, img->yhot); - - xcb_render_free_picture(c, pic); - xcb_free_pixmap(c, pix); - - return cursor; -} - -static xcb_cursor_t -xcb_cursor_images_load_cursor(struct weston_wm *wm, const XcursorImages *images) -{ - /* TODO: treat animated cursors as well */ - if (images->nimage != 1) - return -1; - - return xcb_cursor_image_load_cursor(wm, images->images[0]); -} - -static xcb_cursor_t -xcb_cursor_library_load_cursor(struct weston_wm *wm, const char *file) -{ - xcb_cursor_t cursor; - XcursorImages *images; - char *v = NULL; - int size = 0; - - if (!file) - return 0; - - v = getenv ("XCURSOR_SIZE"); - if (v) - size = atoi(v); - - if (!size) - size = 32; - - images = XcursorLibraryLoadImages (file, NULL, size); - if (!images) - return -1; - - cursor = xcb_cursor_images_load_cursor (wm, images); - XcursorImagesDestroy (images); - - return cursor; -} - -void -dump_property(struct weston_wm *wm, - xcb_atom_t property, xcb_get_property_reply_t *reply) -{ - int32_t *incr_value; - const char *text_value, *name; - xcb_atom_t *atom_value; - int width, len; - uint32_t i; - - width = wm_log_continue("%s: ", get_atom_name(wm->conn, property)); - if (reply == NULL) { - wm_log_continue("(no reply)\n"); - return; - } - - width += wm_log_continue("%s/%d, length %d (value_len %d): ", - get_atom_name(wm->conn, reply->type), - reply->format, - xcb_get_property_value_length(reply), - reply->value_len); - - if (reply->type == wm->atom.incr) { - incr_value = xcb_get_property_value(reply); - wm_log_continue("%d\n", *incr_value); - } else if (reply->type == wm->atom.utf8_string || - reply->type == wm->atom.string) { - text_value = xcb_get_property_value(reply); - if (reply->value_len > 40) - len = 40; - else - len = reply->value_len; - wm_log_continue("\"%.*s\"\n", len, text_value); - } else if (reply->type == XCB_ATOM_ATOM) { - atom_value = xcb_get_property_value(reply); - for (i = 0; i < reply->value_len; i++) { - name = get_atom_name(wm->conn, atom_value[i]); - if (width + strlen(name) + 2 > 78) { - wm_log_continue("\n "); - width = 4; - } else if (i > 0) { - width += wm_log_continue(", "); - } - - width += wm_log_continue("%s", name); - } - wm_log_continue("\n"); - } else { - wm_log_continue("huh?\n"); - } -} - -static void -read_and_dump_property(struct weston_wm *wm, - xcb_window_t window, xcb_atom_t property) -{ - xcb_get_property_reply_t *reply; - xcb_get_property_cookie_t cookie; - - cookie = xcb_get_property(wm->conn, 0, window, - property, XCB_ATOM_ANY, 0, 2048); - reply = xcb_get_property_reply(wm->conn, cookie, NULL); - - dump_property(wm, property, reply); - - free(reply); -} - -/* We reuse some predefined, but otherwise useles atoms */ -#define TYPE_WM_PROTOCOLS XCB_ATOM_CUT_BUFFER0 -#define TYPE_MOTIF_WM_HINTS XCB_ATOM_CUT_BUFFER1 -#define TYPE_NET_WM_STATE XCB_ATOM_CUT_BUFFER2 -#define TYPE_WM_NORMAL_HINTS XCB_ATOM_CUT_BUFFER3 - -static void -weston_wm_window_read_properties(struct weston_wm_window *window) -{ - struct weston_wm *wm = window->wm; - struct weston_shell_interface *shell_interface = - &wm->server->compositor->shell_interface; - -#define F(field) offsetof(struct weston_wm_window, field) - const struct { - xcb_atom_t atom; - xcb_atom_t type; - int offset; - } props[] = { - { XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, F(class) }, - { XCB_ATOM_WM_NAME, XCB_ATOM_STRING, F(name) }, - { XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, F(transient_for) }, - { wm->atom.wm_protocols, TYPE_WM_PROTOCOLS, F(protocols) }, - { wm->atom.wm_normal_hints, TYPE_WM_NORMAL_HINTS, F(protocols) }, - { wm->atom.net_wm_state, TYPE_NET_WM_STATE }, - { wm->atom.net_wm_window_type, XCB_ATOM_ATOM, F(type) }, - { wm->atom.net_wm_name, XCB_ATOM_STRING, F(name) }, - { wm->atom.net_wm_pid, XCB_ATOM_CARDINAL, F(pid) }, - { wm->atom.motif_wm_hints, TYPE_MOTIF_WM_HINTS, 0 }, - { wm->atom.wm_client_machine, XCB_ATOM_WM_CLIENT_MACHINE, F(machine) }, - }; -#undef F - - xcb_get_property_cookie_t cookie[ARRAY_LENGTH(props)]; - xcb_get_property_reply_t *reply; - void *p; - uint32_t *xid; - xcb_atom_t *atom; - uint32_t i; - - if (!window->properties_dirty) - return; - window->properties_dirty = 0; - - for (i = 0; i < ARRAY_LENGTH(props); i++) - cookie[i] = xcb_get_property(wm->conn, - 0, /* delete */ - window->id, - props[i].atom, - XCB_ATOM_ANY, 0, 2048); - - window->decorate = !window->override_redirect; - window->size_hints.flags = 0; - window->motif_hints.flags = 0; - - for (i = 0; i < ARRAY_LENGTH(props); i++) { - reply = xcb_get_property_reply(wm->conn, cookie[i], NULL); - if (!reply) - /* Bad window, typically */ - continue; - if (reply->type == XCB_ATOM_NONE) { - /* No such property */ - free(reply); - continue; - } - - p = ((char *) window + props[i].offset); - - switch (props[i].type) { - case XCB_ATOM_WM_CLIENT_MACHINE: - case XCB_ATOM_STRING: - /* FIXME: We're using this for both string and - utf8_string */ - if (*(char **) p) - free(*(char **) p); - - *(char **) p = - strndup(xcb_get_property_value(reply), - xcb_get_property_value_length(reply)); - break; - case XCB_ATOM_WINDOW: - xid = xcb_get_property_value(reply); - *(struct weston_wm_window **) p = - hash_table_lookup(wm->window_hash, *xid); - break; - case XCB_ATOM_CARDINAL: - case XCB_ATOM_ATOM: - atom = xcb_get_property_value(reply); - *(xcb_atom_t *) p = *atom; - break; - case TYPE_WM_PROTOCOLS: - break; - case TYPE_WM_NORMAL_HINTS: - memcpy(&window->size_hints, - xcb_get_property_value(reply), - sizeof window->size_hints); - break; - case TYPE_NET_WM_STATE: - window->fullscreen = 0; - atom = xcb_get_property_value(reply); - for (i = 0; i < reply->value_len; i++) - if (atom[i] == wm->atom.net_wm_state_fullscreen) - window->fullscreen = 1; - break; - case TYPE_MOTIF_WM_HINTS: - memcpy(&window->motif_hints, - xcb_get_property_value(reply), - sizeof window->motif_hints); - if (window->motif_hints.flags & MWM_HINTS_DECORATIONS) - window->decorate = - window->motif_hints.decorations > 0; - break; - default: - break; - } - free(reply); - } - - if (window->shsurf && window->name) - shell_interface->set_title(window->shsurf, window->name); -} - -static void -weston_wm_window_get_frame_size(struct weston_wm_window *window, - int *width, int *height) -{ - struct theme *t = window->wm->theme; - - if (window->fullscreen) { - *width = window->width; - *height = window->height; - } else if (window->decorate) { - *width = window->width + (t->margin + t->width) * 2; - *height = window->height + - t->margin * 2 + t->width + t->titlebar_height; - } else { - *width = window->width + t->margin * 2; - *height = window->height + t->margin * 2; - } -} - -static void -weston_wm_window_get_child_position(struct weston_wm_window *window, - int *x, int *y) -{ - struct theme *t = window->wm->theme; - - if (window->fullscreen) { - *x = 0; - *y = 0; - } else if (window->decorate) { - *x = t->margin + t->width; - *y = t->margin + t->titlebar_height; - } else { - *x = t->margin; - *y = t->margin; - } -} - -static void -weston_wm_window_send_configure_notify(struct weston_wm_window *window) -{ - xcb_configure_notify_event_t configure_notify; - struct weston_wm *wm = window->wm; - int x, y; - - weston_wm_window_get_child_position(window, &x, &y); - configure_notify.response_type = XCB_CONFIGURE_NOTIFY; - configure_notify.pad0 = 0; - configure_notify.event = window->id; - configure_notify.window = window->id; - configure_notify.above_sibling = XCB_WINDOW_NONE; - configure_notify.x = x; - configure_notify.y = y; - configure_notify.width = window->width; - configure_notify.height = window->height; - configure_notify.border_width = 0; - configure_notify.override_redirect = 0; - configure_notify.pad1 = 0; - - xcb_send_event(wm->conn, 0, window->id, - XCB_EVENT_MASK_STRUCTURE_NOTIFY, - (char *) &configure_notify); -} - -static void -weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_configure_request_event_t *configure_request = - (xcb_configure_request_event_t *) event; - struct weston_wm_window *window; - uint32_t mask, values[16]; - int x, y, width, height, i = 0; - - wm_log("XCB_CONFIGURE_REQUEST (window %d) %d,%d @ %dx%d\n", - configure_request->window, - configure_request->x, configure_request->y, - configure_request->width, configure_request->height); - - window = hash_table_lookup(wm->window_hash, configure_request->window); - - if (window->fullscreen) { - weston_wm_window_send_configure_notify(window); - return; - } - - if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH) - window->width = configure_request->width; - if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT) - window->height = configure_request->height; - - weston_wm_window_get_child_position(window, &x, &y); - values[i++] = x; - values[i++] = y; - values[i++] = window->width; - values[i++] = window->height; - values[i++] = 0; - mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | - XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | - XCB_CONFIG_WINDOW_BORDER_WIDTH; - if (configure_request->value_mask & XCB_CONFIG_WINDOW_SIBLING) { - values[i++] = configure_request->sibling; - mask |= XCB_CONFIG_WINDOW_SIBLING; - } - if (configure_request->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) { - values[i++] = configure_request->stack_mode; - mask |= XCB_CONFIG_WINDOW_STACK_MODE; - } - - xcb_configure_window(wm->conn, window->id, mask, values); - - weston_wm_window_get_frame_size(window, &width, &height); - values[0] = width; - values[1] = height; - mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; - xcb_configure_window(wm->conn, window->frame_id, mask, values); - - weston_wm_window_schedule_repaint(window); -} - -static int -our_resource(struct weston_wm *wm, uint32_t id) -{ - const xcb_setup_t *setup; - - setup = xcb_get_setup(wm->conn); - - return (id & ~setup->resource_id_mask) == setup->resource_id_base; -} - -static void -weston_wm_handle_configure_notify(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_configure_notify_event_t *configure_notify = - (xcb_configure_notify_event_t *) event; - struct weston_wm_window *window; - - wm_log("XCB_CONFIGURE_NOTIFY (window %d) %d,%d @ %dx%d\n", - configure_notify->window, - configure_notify->x, configure_notify->y, - configure_notify->width, configure_notify->height); - - window = hash_table_lookup(wm->window_hash, configure_notify->window); - window->x = configure_notify->x; - window->y = configure_notify->y; - if (window->override_redirect) { - window->width = configure_notify->width; - window->height = configure_notify->height; - } -} - -static void -weston_wm_kill_client(struct wl_listener *listener, void *data) -{ - struct weston_surface *surface = data; - struct weston_wm_window *window = get_wm_window(surface); - char name[1024]; - - if (!window) - return; - - gethostname(name, 1024); - - /* this is only one heuristic to guess the PID of a client is valid, - * assuming it's compliant with icccm and ewmh. Non-compliants and - * remote applications of course fail. */ - if (!strcmp(window->machine, name) && window->pid != 0) - kill(window->pid, SIGKILL); -} - -static void -weston_wm_window_activate(struct wl_listener *listener, void *data) -{ - struct weston_surface *surface = data; - struct weston_wm_window *window = NULL; - struct weston_wm *wm = - container_of(listener, struct weston_wm, activate_listener); - xcb_client_message_event_t client_message; - - if (surface) { - window = get_wm_window(surface); - } - - if (window) { - client_message.response_type = XCB_CLIENT_MESSAGE; - client_message.format = 32; - client_message.window = window->id; - client_message.type = wm->atom.wm_protocols; - client_message.data.data32[0] = wm->atom.wm_take_focus; - client_message.data.data32[1] = XCB_TIME_CURRENT_TIME; - - xcb_send_event(wm->conn, 0, window->id, - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, - (char *) &client_message); - - xcb_set_input_focus (wm->conn, XCB_INPUT_FOCUS_POINTER_ROOT, - window->id, XCB_TIME_CURRENT_TIME); - } else { - xcb_set_input_focus (wm->conn, - XCB_INPUT_FOCUS_POINTER_ROOT, - XCB_NONE, - XCB_TIME_CURRENT_TIME); - } - - if (wm->focus_window) - weston_wm_window_schedule_repaint(wm->focus_window); - wm->focus_window = window; - if (wm->focus_window) - weston_wm_window_schedule_repaint(wm->focus_window); -} - -static void -weston_wm_window_transform(struct wl_listener *listener, void *data) -{ - struct weston_surface *surface = data; - struct weston_wm_window *window = get_wm_window(surface); - struct weston_wm *wm = - container_of(listener, struct weston_wm, transform_listener); - uint32_t mask, values[2]; - - if (!window || !wm) - return; - - if (!weston_surface_is_mapped(surface)) - return; - - if (window->x != surface->geometry.x || - window->y != surface->geometry.y) { - values[0] = surface->geometry.x; - values[1] = surface->geometry.y; - mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y; - - xcb_configure_window(wm->conn, window->frame_id, mask, values); - xcb_flush(wm->conn); - } -} - -#define ICCCM_WITHDRAWN_STATE 0 -#define ICCCM_NORMAL_STATE 1 -#define ICCCM_ICONIC_STATE 3 - -static void -weston_wm_window_set_wm_state(struct weston_wm_window *window, int32_t state) -{ - struct weston_wm *wm = window->wm; - uint32_t property[2]; - - property[0] = state; - property[1] = XCB_WINDOW_NONE; - - xcb_change_property(wm->conn, - XCB_PROP_MODE_REPLACE, - window->id, - wm->atom.wm_state, - wm->atom.wm_state, - 32, /* format */ - 2, property); -} - -static void -weston_wm_window_set_net_wm_state(struct weston_wm_window *window) -{ - struct weston_wm *wm = window->wm; - uint32_t property[1]; - int i; - - i = 0; - if (window->fullscreen) - property[i++] = wm->atom.net_wm_state_fullscreen; - - xcb_change_property(wm->conn, - XCB_PROP_MODE_REPLACE, - window->id, - wm->atom.net_wm_state, - XCB_ATOM_ATOM, - 32, /* format */ - i, property); -} - -static void -weston_wm_window_create_frame(struct weston_wm_window *window) -{ - struct weston_wm *wm = window->wm; - uint32_t values[3]; - int x, y, width, height; - - weston_wm_window_get_frame_size(window, &width, &height); - weston_wm_window_get_child_position(window, &x, &y); - - values[0] = wm->screen->black_pixel; - values[1] = - XCB_EVENT_MASK_KEY_PRESS | - XCB_EVENT_MASK_KEY_RELEASE | - XCB_EVENT_MASK_BUTTON_PRESS | - XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_POINTER_MOTION | - XCB_EVENT_MASK_ENTER_WINDOW | - XCB_EVENT_MASK_LEAVE_WINDOW | - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; - values[2] = wm->colormap; - - window->frame_id = xcb_generate_id(wm->conn); - xcb_create_window(wm->conn, - 32, - window->frame_id, - wm->screen->root, - 0, 0, - width, height, - 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - wm->visual_id, - XCB_CW_BORDER_PIXEL | - XCB_CW_EVENT_MASK | - XCB_CW_COLORMAP, values); - - xcb_reparent_window(wm->conn, window->id, window->frame_id, x, y); - - values[0] = 0; - xcb_configure_window(wm->conn, window->id, - XCB_CONFIG_WINDOW_BORDER_WIDTH, values); - - window->cairo_surface = - cairo_xcb_surface_create_with_xrender_format(wm->conn, - wm->screen, - window->frame_id, - &wm->format_rgba, - width, height); - - hash_table_insert(wm->window_hash, window->frame_id, window); -} - -static void -weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_map_request_event_t *map_request = - (xcb_map_request_event_t *) event; - struct weston_wm_window *window; - - if (our_resource(wm, map_request->window)) { - wm_log("XCB_MAP_REQUEST (window %d, ours)\n", - map_request->window); - return; - } - - window = hash_table_lookup(wm->window_hash, map_request->window); - - weston_wm_window_read_properties(window); - - if (window->frame_id == XCB_WINDOW_NONE) - weston_wm_window_create_frame(window); - - wm_log("XCB_MAP_REQUEST (window %d, %p, frame %d)\n", - window->id, window, window->frame_id); - - weston_wm_window_set_wm_state(window, ICCCM_NORMAL_STATE); - weston_wm_window_set_net_wm_state(window); - - xcb_map_window(wm->conn, map_request->window); - xcb_map_window(wm->conn, window->frame_id); -} - -static void -weston_wm_handle_map_notify(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_map_notify_event_t *map_notify = (xcb_map_notify_event_t *) event; - - if (our_resource(wm, map_notify->window)) { - wm_log("XCB_MAP_NOTIFY (window %d, ours)\n", - map_notify->window); - return; - } - - wm_log("XCB_MAP_NOTIFY (window %d)\n", map_notify->window); -} - -static void -weston_wm_handle_unmap_notify(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_unmap_notify_event_t *unmap_notify = - (xcb_unmap_notify_event_t *) event; - struct weston_wm_window *window; - - wm_log("XCB_UNMAP_NOTIFY (window %d, event %d%s)\n", - unmap_notify->window, - unmap_notify->event, - our_resource(wm, unmap_notify->window) ? ", ours" : ""); - - if (our_resource(wm, unmap_notify->window)) - return; - - if (unmap_notify->response_type & SEND_EVENT_MASK) - /* We just ignore the ICCCM 4.1.4 synthetic unmap notify - * as it may come in after we've destroyed the window. */ - return; - - window = hash_table_lookup(wm->window_hash, unmap_notify->window); - if (wm->focus_window == window) - wm->focus_window = NULL; - if (window->surface) - wl_list_remove(&window->surface_destroy_listener.link); - window->surface = NULL; - window->shsurf = NULL; - xcb_unmap_window(wm->conn, window->frame_id); -} - -static void -weston_wm_window_draw_decoration(void *data) -{ - struct weston_wm_window *window = data; - struct weston_wm *wm = window->wm; - struct theme *t = wm->theme; - cairo_t *cr; - int x, y, width, height; - const char *title; - uint32_t flags = 0; - - weston_wm_window_read_properties(window); - - window->repaint_source = NULL; - - weston_wm_window_get_frame_size(window, &width, &height); - weston_wm_window_get_child_position(window, &x, &y); - - cairo_xcb_surface_set_size(window->cairo_surface, width, height); - cr = cairo_create(window->cairo_surface); - - if (window->fullscreen) { - /* nothing */ - } else if (window->decorate) { - if (wm->focus_window == window) - flags |= THEME_FRAME_ACTIVE; - - if (window->name) - title = window->name; - else - title = "untitled"; - - theme_render_frame(t, cr, width, height, title, flags); - } else { - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba(cr, 0, 0, 0, 0); - cairo_paint(cr); - - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_set_source_rgba(cr, 0, 0, 0, 0.45); - tile_mask(cr, t->shadow, 2, 2, width + 8, height + 8, 64, 64); - } - - cairo_destroy(cr); - - if (window->surface) { - pixman_region32_fini(&window->surface->pending.opaque); - if(window->has_alpha) { - pixman_region32_init(&window->surface->pending.opaque); - } else { - /* We leave an extra pixel around the X window area to - * make sure we don't sample from the undefined alpha - * channel when filtering. */ - pixman_region32_init_rect(&window->surface->pending.opaque, - x - 1, y - 1, - window->width + 2, - window->height + 2); - } - weston_surface_geometry_dirty(window->surface); - - pixman_region32_fini(&window->surface->pending.input); - - if (window->fullscreen) { - pixman_region32_init_rect(&window->surface->pending.input, - 0, 0, window->width, window->height); - } else if (window->decorate) { - pixman_region32_init_rect(&window->surface->pending.input, - t->margin, t->margin, - width - 2 * t->margin, - height - 2 * t->margin); - } - } -} - -static void -weston_wm_window_schedule_repaint(struct weston_wm_window *window) -{ - struct weston_wm *wm = window->wm; - int width, height; - - if (window->frame_id == XCB_WINDOW_NONE) { - if (window->surface != NULL) { - weston_wm_window_get_frame_size(window, &width, &height); - pixman_region32_fini(&window->surface->pending.opaque); - if(window->has_alpha) { - pixman_region32_init(&window->surface->pending.opaque); - } else { - pixman_region32_init_rect(&window->surface->pending.opaque, 0, 0, - width, height); - } - weston_surface_geometry_dirty(window->surface); - } - return; - } - - if (window->repaint_source) - return; - - window->repaint_source = - wl_event_loop_add_idle(wm->server->loop, - weston_wm_window_draw_decoration, - window); -} - -static void -weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_property_notify_event_t *property_notify = - (xcb_property_notify_event_t *) event; - struct weston_wm_window *window; - - window = hash_table_lookup(wm->window_hash, property_notify->window); - if (!window) - return; - - window->properties_dirty = 1; - - wm_log("XCB_PROPERTY_NOTIFY: window %d, ", property_notify->window); - if (property_notify->state == XCB_PROPERTY_DELETE) - wm_log("deleted\n"); - else - read_and_dump_property(wm, property_notify->window, - property_notify->atom); - - if (property_notify->atom == wm->atom.net_wm_name || - property_notify->atom == XCB_ATOM_WM_NAME) - weston_wm_window_schedule_repaint(window); -} - -static void -weston_wm_window_create(struct weston_wm *wm, - xcb_window_t id, int width, int height, int x, int y, int override) -{ - struct weston_wm_window *window; - uint32_t values[1]; - xcb_get_geometry_cookie_t geometry_cookie; - xcb_get_geometry_reply_t *geometry_reply; - - window = zalloc(sizeof *window); - if (window == NULL) { - wm_log("failed to allocate window\n"); - return; - } - - geometry_cookie = xcb_get_geometry(wm->conn, id); - - values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE; - xcb_change_window_attributes(wm->conn, id, XCB_CW_EVENT_MASK, values); - - window->wm = wm; - window->id = id; - window->properties_dirty = 1; - window->override_redirect = override; - window->width = width; - window->height = height; - window->x = x; - window->y = y; - - geometry_reply = xcb_get_geometry_reply(wm->conn, geometry_cookie, NULL); - /* technically we should use XRender and check the visual format's - alpha_mask, but checking depth is simpler and works in all known cases */ - if(geometry_reply != NULL) - window->has_alpha = geometry_reply->depth == 32; - free(geometry_reply); - - hash_table_insert(wm->window_hash, id, window); -} - -static void -weston_wm_window_destroy(struct weston_wm_window *window) -{ - struct weston_wm *wm = window->wm; - - if (window->repaint_source) - wl_event_source_remove(window->repaint_source); - if (window->cairo_surface) - cairo_surface_destroy(window->cairo_surface); - - if (window->frame_id) { - xcb_reparent_window(wm->conn, window->id, wm->wm_window, 0, 0); - xcb_destroy_window(wm->conn, window->frame_id); - weston_wm_window_set_wm_state(window, ICCCM_WITHDRAWN_STATE); - hash_table_remove(wm->window_hash, window->frame_id); - window->frame_id = XCB_WINDOW_NONE; - } - - hash_table_remove(window->wm->window_hash, window->id); - free(window); -} - -static void -weston_wm_handle_create_notify(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_create_notify_event_t *create_notify = - (xcb_create_notify_event_t *) event; - - wm_log("XCB_CREATE_NOTIFY (window %d, width %d, height %d%s%s)\n", - create_notify->window, - create_notify->width, create_notify->height, - create_notify->override_redirect ? ", override" : "", - our_resource(wm, create_notify->window) ? ", ours" : ""); - - if (our_resource(wm, create_notify->window)) - return; - - weston_wm_window_create(wm, create_notify->window, - create_notify->width, create_notify->height, - create_notify->x, create_notify->y, - create_notify->override_redirect); -} - -static void -weston_wm_handle_destroy_notify(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_destroy_notify_event_t *destroy_notify = - (xcb_destroy_notify_event_t *) event; - struct weston_wm_window *window; - - wm_log("XCB_DESTROY_NOTIFY, win %d, event %d%s\n", - destroy_notify->window, - destroy_notify->event, - our_resource(wm, destroy_notify->window) ? ", ours" : ""); - - if (our_resource(wm, destroy_notify->window)) - return; - - window = hash_table_lookup(wm->window_hash, destroy_notify->window); - weston_wm_window_destroy(window); -} - -static void -weston_wm_handle_reparent_notify(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_reparent_notify_event_t *reparent_notify = - (xcb_reparent_notify_event_t *) event; - struct weston_wm_window *window; - - wm_log("XCB_REPARENT_NOTIFY (window %d, parent %d, event %d)\n", - reparent_notify->window, - reparent_notify->parent, - reparent_notify->event); - - if (reparent_notify->parent == wm->screen->root) { - weston_wm_window_create(wm, reparent_notify->window, 10, 10, - reparent_notify->x, reparent_notify->y, - reparent_notify->override_redirect); - } else if (!our_resource(wm, reparent_notify->parent)) { - window = hash_table_lookup(wm->window_hash, - reparent_notify->window); - weston_wm_window_destroy(window); - } -} - -struct weston_seat * -weston_wm_pick_seat(struct weston_wm *wm) -{ - return container_of(wm->server->compositor->seat_list.next, - struct weston_seat, link); -} - -static void -weston_wm_window_handle_moveresize(struct weston_wm_window *window, - xcb_client_message_event_t *client_message) -{ - static const int map[] = { - THEME_LOCATION_RESIZING_TOP_LEFT, - THEME_LOCATION_RESIZING_TOP, - THEME_LOCATION_RESIZING_TOP_RIGHT, - THEME_LOCATION_RESIZING_RIGHT, - THEME_LOCATION_RESIZING_BOTTOM_RIGHT, - THEME_LOCATION_RESIZING_BOTTOM, - THEME_LOCATION_RESIZING_BOTTOM_LEFT, - THEME_LOCATION_RESIZING_LEFT - }; - - struct weston_wm *wm = window->wm; - struct weston_seat *seat = weston_wm_pick_seat(wm); - int detail; - struct weston_shell_interface *shell_interface = - &wm->server->compositor->shell_interface; - - if (seat->pointer->button_count != 1 || - seat->pointer->focus != window->surface) - return; - - detail = client_message->data.data32[2]; - switch (detail) { - case _NET_WM_MOVERESIZE_MOVE: - shell_interface->move(window->shsurf, seat); - break; - case _NET_WM_MOVERESIZE_SIZE_TOPLEFT: - case _NET_WM_MOVERESIZE_SIZE_TOP: - case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT: - case _NET_WM_MOVERESIZE_SIZE_RIGHT: - case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT: - case _NET_WM_MOVERESIZE_SIZE_BOTTOM: - case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT: - case _NET_WM_MOVERESIZE_SIZE_LEFT: - shell_interface->resize(window->shsurf, seat, map[detail]); - break; - case _NET_WM_MOVERESIZE_CANCEL: - break; - } -} - -#define _NET_WM_STATE_REMOVE 0 -#define _NET_WM_STATE_ADD 1 -#define _NET_WM_STATE_TOGGLE 2 - -static int -update_state(int action, int *state) -{ - int new_state, changed; - - switch (action) { - case _NET_WM_STATE_REMOVE: - new_state = 0; - break; - case _NET_WM_STATE_ADD: - new_state = 1; - break; - case _NET_WM_STATE_TOGGLE: - new_state = !*state; - break; - default: - return 0; - } - - changed = (*state != new_state); - *state = new_state; - - return changed; -} - -static void -weston_wm_window_configure(void *data); - -static void -weston_wm_window_handle_state(struct weston_wm_window *window, - xcb_client_message_event_t *client_message) -{ - struct weston_wm *wm = window->wm; - struct weston_shell_interface *shell_interface = - &wm->server->compositor->shell_interface; - uint32_t action, property; - - action = client_message->data.data32[0]; - property = client_message->data.data32[1]; - - if (property == wm->atom.net_wm_state_fullscreen && - update_state(action, &window->fullscreen)) { - weston_wm_window_set_net_wm_state(window); - if (window->fullscreen) { - window->saved_width = window->width; - window->saved_height = window->height; - - if (window->shsurf) - shell_interface->set_fullscreen(window->shsurf, - WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, - 0, NULL); - } else { - shell_interface->set_toplevel(window->shsurf); - window->width = window->saved_width; - window->height = window->saved_height; - weston_wm_window_configure(window); - } - } -} - -static void -weston_wm_handle_client_message(struct weston_wm *wm, - xcb_generic_event_t *event) -{ - xcb_client_message_event_t *client_message = - (xcb_client_message_event_t *) event; - struct weston_wm_window *window; - - window = hash_table_lookup(wm->window_hash, client_message->window); - - wm_log("XCB_CLIENT_MESSAGE (%s %d %d %d %d %d win %d)\n", - get_atom_name(wm->conn, client_message->type), - client_message->data.data32[0], - client_message->data.data32[1], - client_message->data.data32[2], - client_message->data.data32[3], - client_message->data.data32[4], - client_message->window); - - if (client_message->type == wm->atom.net_wm_moveresize) - weston_wm_window_handle_moveresize(window, client_message); - else if (client_message->type == wm->atom.net_wm_state) - weston_wm_window_handle_state(window, client_message); -} - -enum cursor_type { - XWM_CURSOR_TOP, - XWM_CURSOR_BOTTOM, - XWM_CURSOR_LEFT, - XWM_CURSOR_RIGHT, - XWM_CURSOR_TOP_LEFT, - XWM_CURSOR_TOP_RIGHT, - XWM_CURSOR_BOTTOM_LEFT, - XWM_CURSOR_BOTTOM_RIGHT, - XWM_CURSOR_LEFT_PTR, -}; - -/* - * The following correspondences between file names and cursors was copied - * from: https://bugs.kde.org/attachment.cgi?id=67313 - */ - -static const char *bottom_left_corners[] = { - "bottom_left_corner", - "sw-resize", - "size_bdiag" -}; - -static const char *bottom_right_corners[] = { - "bottom_right_corner", - "se-resize", - "size_fdiag" -}; - -static const char *bottom_sides[] = { - "bottom_side", - "s-resize", - "size_ver" -}; - -static const char *left_ptrs[] = { - "left_ptr", - "default", - "top_left_arrow", - "left-arrow" -}; - -static const char *left_sides[] = { - "left_side", - "w-resize", - "size_hor" -}; - -static const char *right_sides[] = { - "right_side", - "e-resize", - "size_hor" -}; - -static const char *top_left_corners[] = { - "top_left_corner", - "nw-resize", - "size_fdiag" -}; - -static const char *top_right_corners[] = { - "top_right_corner", - "ne-resize", - "size_bdiag" -}; - -static const char *top_sides[] = { - "top_side", - "n-resize", - "size_ver" -}; - -struct cursor_alternatives { - const char **names; - size_t count; -}; - -static const struct cursor_alternatives cursors[] = { - {top_sides, ARRAY_LENGTH(top_sides)}, - {bottom_sides, ARRAY_LENGTH(bottom_sides)}, - {left_sides, ARRAY_LENGTH(left_sides)}, - {right_sides, ARRAY_LENGTH(right_sides)}, - {top_left_corners, ARRAY_LENGTH(top_left_corners)}, - {top_right_corners, ARRAY_LENGTH(top_right_corners)}, - {bottom_left_corners, ARRAY_LENGTH(bottom_left_corners)}, - {bottom_right_corners, ARRAY_LENGTH(bottom_right_corners)}, - {left_ptrs, ARRAY_LENGTH(left_ptrs)}, -}; - -static void -weston_wm_create_cursors(struct weston_wm *wm) -{ - const char *name; - int i, count = ARRAY_LENGTH(cursors); - size_t j; - - wm->cursors = malloc(count * sizeof(xcb_cursor_t)); - for (i = 0; i < count; i++) { - for (j = 0; j < cursors[i].count; j++) { - name = cursors[i].names[j]; - wm->cursors[i] = - xcb_cursor_library_load_cursor(wm, name); - if (wm->cursors[i] != (xcb_cursor_t)-1) - break; - } - } - - wm->last_cursor = -1; -} - -static void -weston_wm_destroy_cursors(struct weston_wm *wm) -{ - uint8_t i; - - for (i = 0; i < ARRAY_LENGTH(cursors); i++) - xcb_free_cursor(wm->conn, wm->cursors[i]); - - free(wm->cursors); -} - -static int -get_cursor_for_location(struct theme *t, int width, int height, int x, int y) -{ - int location = theme_get_location(t, x, y, width, height, 0); - - switch (location) { - case THEME_LOCATION_RESIZING_TOP: - return XWM_CURSOR_TOP; - case THEME_LOCATION_RESIZING_BOTTOM: - return XWM_CURSOR_BOTTOM; - case THEME_LOCATION_RESIZING_LEFT: - return XWM_CURSOR_LEFT; - case THEME_LOCATION_RESIZING_RIGHT: - return XWM_CURSOR_RIGHT; - case THEME_LOCATION_RESIZING_TOP_LEFT: - return XWM_CURSOR_TOP_LEFT; - case THEME_LOCATION_RESIZING_TOP_RIGHT: - return XWM_CURSOR_TOP_RIGHT; - case THEME_LOCATION_RESIZING_BOTTOM_LEFT: - return XWM_CURSOR_BOTTOM_LEFT; - case THEME_LOCATION_RESIZING_BOTTOM_RIGHT: - return XWM_CURSOR_BOTTOM_RIGHT; - case THEME_LOCATION_EXTERIOR: - case THEME_LOCATION_TITLEBAR: - default: - return XWM_CURSOR_LEFT_PTR; - } -} - -static void -weston_wm_window_set_cursor(struct weston_wm *wm, xcb_window_t window_id, - int cursor) -{ - uint32_t cursor_value_list; - - if (wm->last_cursor == cursor) - return; - - wm->last_cursor = cursor; - - cursor_value_list = wm->cursors[cursor]; - xcb_change_window_attributes (wm->conn, window_id, - XCB_CW_CURSOR, &cursor_value_list); - xcb_flush(wm->conn); -} - -static void -weston_wm_handle_button(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_button_press_event_t *button = (xcb_button_press_event_t *) event; - struct weston_shell_interface *shell_interface = - &wm->server->compositor->shell_interface; - struct weston_seat *seat = weston_wm_pick_seat(wm); - struct weston_wm_window *window; - enum theme_location location; - struct theme *t = wm->theme; - int width, height; - - wm_log("XCB_BUTTON_%s (detail %d)\n", - button->response_type == XCB_BUTTON_PRESS ? - "PRESS" : "RELEASE", button->detail); - - window = hash_table_lookup(wm->window_hash, button->event); - weston_wm_window_get_frame_size(window, &width, &height); - - if (button->response_type == XCB_BUTTON_PRESS && - button->detail == 1) { - location = theme_get_location(t, - button->event_x, - button->event_y, - width, height, 0); - - switch (location) { - case THEME_LOCATION_TITLEBAR: - shell_interface->move(window->shsurf, seat); - break; - case THEME_LOCATION_RESIZING_TOP: - case THEME_LOCATION_RESIZING_BOTTOM: - case THEME_LOCATION_RESIZING_LEFT: - case THEME_LOCATION_RESIZING_RIGHT: - case THEME_LOCATION_RESIZING_TOP_LEFT: - case THEME_LOCATION_RESIZING_TOP_RIGHT: - case THEME_LOCATION_RESIZING_BOTTOM_LEFT: - case THEME_LOCATION_RESIZING_BOTTOM_RIGHT: - shell_interface->resize(window->shsurf, - seat, location); - break; - default: - break; - } - } -} - -static void -weston_wm_handle_motion(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *) event; - struct weston_wm_window *window; - int cursor, width, height; - - window = hash_table_lookup(wm->window_hash, motion->event); - if (!window || !window->decorate) - return; - - weston_wm_window_get_frame_size(window, &width, &height); - cursor = get_cursor_for_location(wm->theme, width, height, - motion->event_x, motion->event_y); - - weston_wm_window_set_cursor(wm, window->frame_id, cursor); -} - -static void -weston_wm_handle_enter(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *) event; - struct weston_wm_window *window; - int cursor, width, height; - - window = hash_table_lookup(wm->window_hash, enter->event); - if (!window || !window->decorate) - return; - - weston_wm_window_get_frame_size(window, &width, &height); - cursor = get_cursor_for_location(wm->theme, width, height, - enter->event_x, enter->event_y); - - weston_wm_window_set_cursor(wm, window->frame_id, cursor); -} - -static void -weston_wm_handle_leave(struct weston_wm *wm, xcb_generic_event_t *event) -{ - xcb_leave_notify_event_t *leave = (xcb_leave_notify_event_t *) event; - struct weston_wm_window *window; - - window = hash_table_lookup(wm->window_hash, leave->event); - if (!window || !window->decorate) - return; - - weston_wm_window_set_cursor(wm, window->frame_id, XWM_CURSOR_LEFT_PTR); -} - -static int -weston_wm_handle_event(int fd, uint32_t mask, void *data) -{ - struct weston_wm *wm = data; - xcb_generic_event_t *event; - int count = 0; - - while (event = xcb_poll_for_event(wm->conn), event != NULL) { - if (weston_wm_handle_selection_event(wm, event)) { - free(event); - count++; - continue; - } - - if (weston_wm_handle_dnd_event(wm, event)) { - free(event); - count++; - continue; - } - - switch (EVENT_TYPE(event)) { - case XCB_BUTTON_PRESS: - case XCB_BUTTON_RELEASE: - weston_wm_handle_button(wm, event); - break; - case XCB_ENTER_NOTIFY: - weston_wm_handle_enter(wm, event); - break; - case XCB_LEAVE_NOTIFY: - weston_wm_handle_leave(wm, event); - break; - case XCB_MOTION_NOTIFY: - weston_wm_handle_motion(wm, event); - break; - case XCB_CREATE_NOTIFY: - weston_wm_handle_create_notify(wm, event); - break; - case XCB_MAP_REQUEST: - weston_wm_handle_map_request(wm, event); - break; - case XCB_MAP_NOTIFY: - weston_wm_handle_map_notify(wm, event); - break; - case XCB_UNMAP_NOTIFY: - weston_wm_handle_unmap_notify(wm, event); - break; - case XCB_REPARENT_NOTIFY: - weston_wm_handle_reparent_notify(wm, event); - break; - case XCB_CONFIGURE_REQUEST: - weston_wm_handle_configure_request(wm, event); - break; - case XCB_CONFIGURE_NOTIFY: - weston_wm_handle_configure_notify(wm, event); - break; - case XCB_DESTROY_NOTIFY: - weston_wm_handle_destroy_notify(wm, event); - break; - case XCB_MAPPING_NOTIFY: - wm_log("XCB_MAPPING_NOTIFY\n"); - break; - case XCB_PROPERTY_NOTIFY: - weston_wm_handle_property_notify(wm, event); - break; - case XCB_CLIENT_MESSAGE: - weston_wm_handle_client_message(wm, event); - break; - } - - free(event); - count++; - } - - xcb_flush(wm->conn); - - return count; -} - -static void -weston_wm_get_visual_and_colormap(struct weston_wm *wm) -{ - xcb_depth_iterator_t d_iter; - xcb_visualtype_iterator_t vt_iter; - xcb_visualtype_t *visualtype; - - d_iter = xcb_screen_allowed_depths_iterator(wm->screen); - visualtype = NULL; - while (d_iter.rem > 0) { - if (d_iter.data->depth == 32) { - vt_iter = xcb_depth_visuals_iterator(d_iter.data); - visualtype = vt_iter.data; - break; - } - - xcb_depth_next(&d_iter); - } - - if (visualtype == NULL) { - weston_log("no 32 bit visualtype\n"); - return; - } - - wm->visual_id = visualtype->visual_id; - wm->colormap = xcb_generate_id(wm->conn); - xcb_create_colormap(wm->conn, XCB_COLORMAP_ALLOC_NONE, - wm->colormap, wm->screen->root, wm->visual_id); -} - -static void -weston_wm_get_resources(struct weston_wm *wm) -{ - -#define F(field) offsetof(struct weston_wm, field) - - static const struct { const char *name; int offset; } atoms[] = { - { "WM_PROTOCOLS", F(atom.wm_protocols) }, - { "WM_NORMAL_HINTS", F(atom.wm_normal_hints) }, - { "WM_TAKE_FOCUS", F(atom.wm_take_focus) }, - { "WM_DELETE_WINDOW", F(atom.wm_delete_window) }, - { "WM_STATE", F(atom.wm_state) }, - { "WM_S0", F(atom.wm_s0) }, - { "WM_CLIENT_MACHINE", F(atom.wm_client_machine) }, - { "_NET_WM_CM_S0", F(atom.net_wm_cm_s0) }, - { "_NET_WM_NAME", F(atom.net_wm_name) }, - { "_NET_WM_PID", F(atom.net_wm_pid) }, - { "_NET_WM_ICON", F(atom.net_wm_icon) }, - { "_NET_WM_STATE", F(atom.net_wm_state) }, - { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) }, - { "_NET_WM_USER_TIME", F(atom.net_wm_user_time) }, - { "_NET_WM_ICON_NAME", F(atom.net_wm_icon_name) }, - { "_NET_WM_WINDOW_TYPE", F(atom.net_wm_window_type) }, - - { "_NET_WM_WINDOW_TYPE_DESKTOP", F(atom.net_wm_window_type_desktop) }, - { "_NET_WM_WINDOW_TYPE_DOCK", F(atom.net_wm_window_type_dock) }, - { "_NET_WM_WINDOW_TYPE_TOOLBAR", F(atom.net_wm_window_type_toolbar) }, - { "_NET_WM_WINDOW_TYPE_MENU", F(atom.net_wm_window_type_menu) }, - { "_NET_WM_WINDOW_TYPE_UTILITY", F(atom.net_wm_window_type_utility) }, - { "_NET_WM_WINDOW_TYPE_SPLASH", F(atom.net_wm_window_type_splash) }, - { "_NET_WM_WINDOW_TYPE_DIALOG", F(atom.net_wm_window_type_dialog) }, - { "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", F(atom.net_wm_window_type_dropdown) }, - { "_NET_WM_WINDOW_TYPE_POPUP_MENU", F(atom.net_wm_window_type_popup) }, - { "_NET_WM_WINDOW_TYPE_TOOLTIP", F(atom.net_wm_window_type_tooltip) }, - { "_NET_WM_WINDOW_TYPE_NOTIFICATION", F(atom.net_wm_window_type_notification) }, - { "_NET_WM_WINDOW_TYPE_COMBO", F(atom.net_wm_window_type_combo) }, - { "_NET_WM_WINDOW_TYPE_DND", F(atom.net_wm_window_type_dnd) }, - { "_NET_WM_WINDOW_TYPE_NORMAL", F(atom.net_wm_window_type_normal) }, - - { "_NET_WM_MOVERESIZE", F(atom.net_wm_moveresize) }, - { "_NET_SUPPORTING_WM_CHECK", - F(atom.net_supporting_wm_check) }, - { "_NET_SUPPORTED", F(atom.net_supported) }, - { "_MOTIF_WM_HINTS", F(atom.motif_wm_hints) }, - { "CLIPBOARD", F(atom.clipboard) }, - { "CLIPBOARD_MANAGER", F(atom.clipboard_manager) }, - { "TARGETS", F(atom.targets) }, - { "UTF8_STRING", F(atom.utf8_string) }, - { "_WL_SELECTION", F(atom.wl_selection) }, - { "INCR", F(atom.incr) }, - { "TIMESTAMP", F(atom.timestamp) }, - { "MULTIPLE", F(atom.multiple) }, - { "UTF8_STRING" , F(atom.utf8_string) }, - { "COMPOUND_TEXT", F(atom.compound_text) }, - { "TEXT", F(atom.text) }, - { "STRING", F(atom.string) }, - { "text/plain;charset=utf-8", F(atom.text_plain_utf8) }, - { "text/plain", F(atom.text_plain) }, - { "XdndSelection", F(atom.xdnd_selection) }, - { "XdndAware", F(atom.xdnd_aware) }, - { "XdndEnter", F(atom.xdnd_enter) }, - { "XdndLeave", F(atom.xdnd_leave) }, - { "XdndDrop", F(atom.xdnd_drop) }, - { "XdndStatus", F(atom.xdnd_status) }, - { "XdndFinished", F(atom.xdnd_finished) }, - { "XdndTypeList", F(atom.xdnd_type_list) }, - { "XdndActionCopy", F(atom.xdnd_action_copy) } - }; -#undef F - - xcb_xfixes_query_version_cookie_t xfixes_cookie; - xcb_xfixes_query_version_reply_t *xfixes_reply; - xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)]; - xcb_intern_atom_reply_t *reply; - xcb_render_query_pict_formats_reply_t *formats_reply; - xcb_render_query_pict_formats_cookie_t formats_cookie; - xcb_render_pictforminfo_t *formats; - uint32_t i; - - xcb_prefetch_extension_data (wm->conn, &xcb_xfixes_id); - xcb_prefetch_extension_data (wm->conn, &xcb_composite_id); - - formats_cookie = xcb_render_query_pict_formats(wm->conn); - - for (i = 0; i < ARRAY_LENGTH(atoms); i++) - cookies[i] = xcb_intern_atom (wm->conn, 0, - strlen(atoms[i].name), - atoms[i].name); - - for (i = 0; i < ARRAY_LENGTH(atoms); i++) { - reply = xcb_intern_atom_reply (wm->conn, cookies[i], NULL); - *(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom; - free(reply); - } - - wm->xfixes = xcb_get_extension_data(wm->conn, &xcb_xfixes_id); - if (!wm->xfixes || !wm->xfixes->present) - weston_log("xfixes not available\n"); - - xfixes_cookie = xcb_xfixes_query_version(wm->conn, - XCB_XFIXES_MAJOR_VERSION, - XCB_XFIXES_MINOR_VERSION); - xfixes_reply = xcb_xfixes_query_version_reply(wm->conn, - xfixes_cookie, NULL); - - weston_log("xfixes version: %d.%d\n", - xfixes_reply->major_version, xfixes_reply->minor_version); - - free(xfixes_reply); - - formats_reply = xcb_render_query_pict_formats_reply(wm->conn, - formats_cookie, 0); - if (formats_reply == NULL) - return; - - formats = xcb_render_query_pict_formats_formats(formats_reply); - for (i = 0; i < formats_reply->num_formats; i++) { - if (formats[i].direct.red_mask != 0xff && - formats[i].direct.red_shift != 16) - continue; - if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT && - formats[i].depth == 24) - wm->format_rgb = formats[i]; - if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT && - formats[i].depth == 32 && - formats[i].direct.alpha_mask == 0xff && - formats[i].direct.alpha_shift == 24) - wm->format_rgba = formats[i]; - } - - free(formats_reply); -} - -static void -weston_wm_create_wm_window(struct weston_wm *wm) -{ - static const char name[] = "Weston WM"; - - wm->wm_window = xcb_generate_id(wm->conn); - xcb_create_window(wm->conn, - XCB_COPY_FROM_PARENT, - wm->wm_window, - wm->screen->root, - 0, 0, - 10, 10, - 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - wm->screen->root_visual, - 0, NULL); - - xcb_change_property(wm->conn, - XCB_PROP_MODE_REPLACE, - wm->wm_window, - wm->atom.net_supporting_wm_check, - XCB_ATOM_WINDOW, - 32, /* format */ - 1, &wm->wm_window); - - xcb_change_property(wm->conn, - XCB_PROP_MODE_REPLACE, - wm->wm_window, - wm->atom.net_wm_name, - wm->atom.utf8_string, - 8, /* format */ - strlen(name), name); - - xcb_change_property(wm->conn, - XCB_PROP_MODE_REPLACE, - wm->screen->root, - wm->atom.net_supporting_wm_check, - XCB_ATOM_WINDOW, - 32, /* format */ - 1, &wm->wm_window); - - /* Claim the WM_S0 selection even though we don't suport - * the --replace functionality. */ - xcb_set_selection_owner(wm->conn, - wm->wm_window, - wm->atom.wm_s0, - XCB_TIME_CURRENT_TIME); - - xcb_set_selection_owner(wm->conn, - wm->wm_window, - wm->atom.net_wm_cm_s0, - XCB_TIME_CURRENT_TIME); -} - -struct weston_wm * -weston_wm_create(struct weston_xserver *wxs) -{ - struct weston_wm *wm; - struct wl_event_loop *loop; - xcb_screen_iterator_t s; - uint32_t values[1]; - int sv[2]; - xcb_atom_t supported[3]; - - wm = zalloc(sizeof *wm); - if (wm == NULL) - return NULL; - - wm->server = wxs; - wm->window_hash = hash_table_create(); - if (wm->window_hash == NULL) { - free(wm); - return NULL; - } - - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { - weston_log("socketpair failed\n"); - hash_table_destroy(wm->window_hash); - free(wm); - return NULL; - } - - xserver_send_client(wxs->resource, sv[1]); - wl_client_flush(wl_resource_get_client(wxs->resource)); - close(sv[1]); - - /* xcb_connect_to_fd takes ownership of the fd. */ - wm->conn = xcb_connect_to_fd(sv[0], NULL); - if (xcb_connection_has_error(wm->conn)) { - weston_log("xcb_connect_to_fd failed\n"); - close(sv[0]); - hash_table_destroy(wm->window_hash); - free(wm); - return NULL; - } - - s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn)); - wm->screen = s.data; - - loop = wl_display_get_event_loop(wxs->wl_display); - wm->source = - wl_event_loop_add_fd(loop, sv[0], - WL_EVENT_READABLE, - weston_wm_handle_event, wm); - wl_event_source_check(wm->source); - - weston_wm_get_resources(wm); - weston_wm_get_visual_and_colormap(wm); - - values[0] = - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | - XCB_EVENT_MASK_PROPERTY_CHANGE; - xcb_change_window_attributes(wm->conn, wm->screen->root, - XCB_CW_EVENT_MASK, values); - - xcb_composite_redirect_subwindows(wm->conn, wm->screen->root, - XCB_COMPOSITE_REDIRECT_MANUAL); - - wm->theme = theme_create(); - - weston_wm_create_wm_window(wm); - - supported[0] = wm->atom.net_wm_moveresize; - supported[1] = wm->atom.net_wm_state; - supported[2] = wm->atom.net_wm_state_fullscreen; - xcb_change_property(wm->conn, - XCB_PROP_MODE_REPLACE, - wm->screen->root, - wm->atom.net_supported, - XCB_ATOM_ATOM, - 32, /* format */ - ARRAY_LENGTH(supported), supported); - - weston_wm_selection_init(wm); - - weston_wm_dnd_init(wm); - - xcb_flush(wm->conn); - - wm->activate_listener.notify = weston_wm_window_activate; - wl_signal_add(&wxs->compositor->activate_signal, - &wm->activate_listener); - wm->transform_listener.notify = weston_wm_window_transform; - wl_signal_add(&wxs->compositor->transform_signal, - &wm->transform_listener); - wm->kill_listener.notify = weston_wm_kill_client; - wl_signal_add(&wxs->compositor->kill_signal, - &wm->kill_listener); - - weston_wm_create_cursors(wm); - weston_wm_window_set_cursor(wm, wm->screen->root, XWM_CURSOR_LEFT_PTR); - - weston_log("created wm\n"); - - return wm; -} - -void -weston_wm_destroy(struct weston_wm *wm) -{ - /* FIXME: Free windows in hash. */ - hash_table_destroy(wm->window_hash); - weston_wm_destroy_cursors(wm); - xcb_disconnect(wm->conn); - wl_event_source_remove(wm->source); - wl_list_remove(&wm->selection_listener.link); - wl_list_remove(&wm->activate_listener.link); - wl_list_remove(&wm->kill_listener.link); - wl_list_remove(&wm->transform_listener.link); - - free(wm); -} - -static void -surface_destroy(struct wl_listener *listener, void *data) -{ - struct weston_wm_window *window = - container_of(listener, - struct weston_wm_window, surface_destroy_listener); - - wm_log("surface for xid %d destroyed\n", window->id); - - /* This should have been freed by the shell. - Don't try to use it later. */ - window->shsurf = NULL; - window->surface = NULL; -} - -static struct weston_wm_window * -get_wm_window(struct weston_surface *surface) -{ - struct wl_listener *listener; - - listener = wl_signal_get(&surface->destroy_signal, surface_destroy); - if (listener) - return container_of(listener, struct weston_wm_window, - surface_destroy_listener); - - return NULL; -} - -static void -weston_wm_window_configure(void *data) -{ - struct weston_wm_window *window = data; - struct weston_wm *wm = window->wm; - uint32_t values[4]; - int x, y, width, height; - - weston_wm_window_get_child_position(window, &x, &y); - values[0] = x; - values[1] = y; - values[2] = window->width; - values[3] = window->height; - xcb_configure_window(wm->conn, - window->id, - XCB_CONFIG_WINDOW_X | - XCB_CONFIG_WINDOW_Y | - XCB_CONFIG_WINDOW_WIDTH | - XCB_CONFIG_WINDOW_HEIGHT, - values); - - weston_wm_window_get_frame_size(window, &width, &height); - values[0] = width; - values[1] = height; - xcb_configure_window(wm->conn, - window->frame_id, - XCB_CONFIG_WINDOW_WIDTH | - XCB_CONFIG_WINDOW_HEIGHT, - values); - - window->configure_source = NULL; - - weston_wm_window_schedule_repaint(window); -} - -static void -send_configure(struct weston_surface *surface, - uint32_t edges, int32_t width, int32_t height) -{ - struct weston_wm_window *window = get_wm_window(surface); - struct weston_wm *wm = window->wm; - struct theme *t = window->wm->theme; - int vborder, hborder; - - if (window->fullscreen) { - hborder = 0; - vborder = 0; - } else if (window->decorate) { - hborder = 2 * (t->margin + t->width); - vborder = 2 * t->margin + t->titlebar_height + t->width; - } else { - hborder = 2 * t->margin; - vborder = 2 * t->margin; - } - - if (width > hborder) - window->width = width - hborder; - else - window->width = 1; - - if (height > vborder) - window->height = height - vborder; - else - window->height = 1; - - if (window->configure_source) - return; - - window->configure_source = - wl_event_loop_add_idle(wm->server->loop, - weston_wm_window_configure, window); -} - -static const struct weston_shell_client shell_client = { - send_configure -}; - -static int -legacy_fullscreen(struct weston_wm *wm, - struct weston_wm_window *window, - struct weston_output **output_ret) -{ - struct weston_compositor *compositor = wm->server->compositor; - struct weston_output *output; - uint32_t minmax = PMinSize | PMaxSize; - int matching_size; - - /* Heuristics for detecting legacy fullscreen windows... */ - - wl_list_for_each(output, &compositor->output_list, link) { - if (output->x == window->x && - output->y == window->y && - output->width == window->width && - output->height == window->height && - window->override_redirect) { - *output_ret = output; - return 1; - } - - matching_size = 0; - if ((window->size_hints.flags & (USSize |PSize)) && - window->size_hints.width == output->width && - window->size_hints.height == output->height) - matching_size = 1; - if ((window->size_hints.flags & minmax) == minmax && - window->size_hints.min_width == output->width && - window->size_hints.min_height == output->height && - window->size_hints.max_width == output->width && - window->size_hints.max_height == output->height) - matching_size = 1; - - if (matching_size && !window->decorate && - (window->size_hints.flags & (USPosition | PPosition)) && - window->size_hints.x == output->x && - window->size_hints.y == output->y) { - *output_ret = output; - return 1; - } - } - - return 0; -} -static void -xserver_map_shell_surface(struct weston_wm *wm, - struct weston_wm_window *window) -{ - struct weston_shell_interface *shell_interface = - &wm->server->compositor->shell_interface; - struct weston_output *output; - - if (!shell_interface->create_shell_surface) - return; - - window->shsurf = - shell_interface->create_shell_surface(shell_interface->shell, - window->surface, - &shell_client); - - if (window->name) - shell_interface->set_title(window->shsurf, window->name); - - if (window->fullscreen) { - window->saved_width = window->width; - window->saved_height = window->height; - shell_interface->set_fullscreen(window->shsurf, - WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, - 0, NULL); - return; - } else if (legacy_fullscreen(wm, window, &output)) { - window->fullscreen = 1; - shell_interface->set_fullscreen(window->shsurf, - WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, - 0, output); - } else if (!window->override_redirect && !window->transient_for) { - shell_interface->set_toplevel(window->shsurf); - return; - } else { - shell_interface->set_xwayland(window->shsurf, - window->x, - window->y, - WL_SHELL_SURFACE_TRANSIENT_INACTIVE); - } -} - -static void -xserver_set_window_id(struct wl_client *client, struct wl_resource *resource, - struct wl_resource *surface_resource, uint32_t id) -{ - struct weston_xserver *wxs = wl_resource_get_user_data(resource); - struct weston_wm *wm = wxs->wm; - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - struct weston_wm_window *window; - - if (client != wxs->client) - return; - - window = hash_table_lookup(wm->window_hash, id); - if (window == NULL) { - weston_log("set_window_id for unknown window %d\n", id); - return; - } - - wm_log("set_window_id %d for surface %p\n", id, surface); - - weston_wm_window_read_properties(window); - - /* A weston_wm_window may have many different surfaces assigned - * throughout its life, so we must make sure to remove the listener - * from the old surface signal list. */ - if (window->surface) - wl_list_remove(&window->surface_destroy_listener.link); - - window->surface = (struct weston_surface *) surface; - window->surface_destroy_listener.notify = surface_destroy; - wl_signal_add(&surface->destroy_signal, - &window->surface_destroy_listener); - - weston_wm_window_schedule_repaint(window); - xserver_map_shell_surface(wm, window); -} - -const struct xserver_interface xserver_implementation = { - xserver_set_window_id -}; diff --git a/src/xwayland/xwayland.h b/src/xwayland/xwayland.h deleted file mode 100644 index 77262e8f..00000000 --- a/src/xwayland/xwayland.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright © 2012 Intel Corporation - * - * Permission to use, copy, modify, distribute, and sell this software and - * its documentation for any purpose is hereby granted without fee, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of the copyright holders not be used in - * advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. The copyright holders make - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#include "../compositor.h" - -#define SEND_EVENT_MASK (0x80) -#define EVENT_TYPE(event) ((event)->response_type & ~SEND_EVENT_MASK) - -struct weston_xserver { - struct wl_display *wl_display; - struct wl_event_loop *loop; - struct wl_event_source *sigchld_source; - int abstract_fd; - struct wl_event_source *abstract_source; - int unix_fd; - struct wl_event_source *unix_source; - int display; - struct weston_process process; - struct wl_resource *resource; - struct wl_client *client; - struct weston_compositor *compositor; - struct weston_wm *wm; - struct wl_listener destroy_listener; -}; - -struct weston_wm { - xcb_connection_t *conn; - const xcb_query_extension_reply_t *xfixes; - struct wl_event_source *source; - xcb_screen_t *screen; - struct hash_table *window_hash; - struct weston_xserver *server; - xcb_window_t wm_window; - struct weston_wm_window *focus_window; - struct theme *theme; - xcb_cursor_t *cursors; - int last_cursor; - xcb_render_pictforminfo_t format_rgb, format_rgba; - xcb_visualid_t visual_id; - xcb_colormap_t colormap; - struct wl_listener activate_listener; - struct wl_listener transform_listener; - struct wl_listener kill_listener; - - xcb_window_t selection_window; - xcb_window_t selection_owner; - int incr; - int data_source_fd; - struct wl_event_source *property_source; - xcb_get_property_reply_t *property_reply; - int property_start; - struct wl_array source_data; - xcb_selection_request_event_t selection_request; - xcb_atom_t selection_target; - xcb_timestamp_t selection_timestamp; - int selection_property_set; - int flush_property_on_delete; - struct wl_listener selection_listener; - - xcb_window_t dnd_window; - xcb_window_t dnd_owner; - - struct { - xcb_atom_t wm_protocols; - xcb_atom_t wm_normal_hints; - xcb_atom_t wm_take_focus; - xcb_atom_t wm_delete_window; - xcb_atom_t wm_state; - xcb_atom_t wm_s0; - xcb_atom_t wm_client_machine; - xcb_atom_t net_wm_cm_s0; - xcb_atom_t net_wm_name; - xcb_atom_t net_wm_pid; - xcb_atom_t net_wm_icon; - xcb_atom_t net_wm_state; - xcb_atom_t net_wm_state_fullscreen; - xcb_atom_t net_wm_user_time; - xcb_atom_t net_wm_icon_name; - xcb_atom_t net_wm_window_type; - xcb_atom_t net_wm_window_type_desktop; - xcb_atom_t net_wm_window_type_dock; - xcb_atom_t net_wm_window_type_toolbar; - xcb_atom_t net_wm_window_type_menu; - xcb_atom_t net_wm_window_type_utility; - xcb_atom_t net_wm_window_type_splash; - xcb_atom_t net_wm_window_type_dialog; - xcb_atom_t net_wm_window_type_dropdown; - xcb_atom_t net_wm_window_type_popup; - xcb_atom_t net_wm_window_type_tooltip; - xcb_atom_t net_wm_window_type_notification; - xcb_atom_t net_wm_window_type_combo; - xcb_atom_t net_wm_window_type_dnd; - xcb_atom_t net_wm_window_type_normal; - xcb_atom_t net_wm_moveresize; - xcb_atom_t net_supporting_wm_check; - xcb_atom_t net_supported; - xcb_atom_t motif_wm_hints; - xcb_atom_t clipboard; - xcb_atom_t clipboard_manager; - xcb_atom_t targets; - xcb_atom_t utf8_string; - xcb_atom_t wl_selection; - xcb_atom_t incr; - xcb_atom_t timestamp; - xcb_atom_t multiple; - xcb_atom_t compound_text; - xcb_atom_t text; - xcb_atom_t string; - xcb_atom_t text_plain_utf8; - xcb_atom_t text_plain; - xcb_atom_t xdnd_selection; - xcb_atom_t xdnd_aware; - xcb_atom_t xdnd_enter; - xcb_atom_t xdnd_leave; - xcb_atom_t xdnd_drop; - xcb_atom_t xdnd_status; - xcb_atom_t xdnd_finished; - xcb_atom_t xdnd_type_list; - xcb_atom_t xdnd_action_copy; - } atom; -}; - -void -dump_property(struct weston_wm *wm, xcb_atom_t property, - xcb_get_property_reply_t *reply); - -const char * -get_atom_name(xcb_connection_t *c, xcb_atom_t atom); - -void -weston_wm_selection_init(struct weston_wm *wm); -int -weston_wm_handle_selection_event(struct weston_wm *wm, - xcb_generic_event_t *event); - -extern const struct xserver_interface xserver_implementation; - -struct weston_wm * -weston_wm_create(struct weston_xserver *wxs); -void -weston_wm_destroy(struct weston_wm *wm); - -struct weston_seat * -weston_wm_pick_seat(struct weston_wm *wm); - -int -weston_wm_handle_dnd_event(struct weston_wm *wm, - xcb_generic_event_t *event); -void -weston_wm_dnd_init(struct weston_wm *wm); diff --git a/src/zoom.c b/src/zoom.c index 220b2b6e..622c0d7c 100644 --- a/src/zoom.c +++ b/src/zoom.c @@ -27,97 +27,6 @@ #include "compositor.h" #include "text-cursor-position-server-protocol.h" -struct text_cursor_position { - struct weston_compositor *ec; - struct wl_global *global; - struct wl_listener destroy_listener; -}; - -static void -text_cursor_position_notify(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *surface_resource, - wl_fixed_t x, wl_fixed_t y) -{ - struct weston_surface *surface = - wl_resource_get_user_data(surface_resource); - - weston_text_cursor_position_notify(surface, x, y); -} - -struct text_cursor_position_interface text_cursor_position_implementation = { - text_cursor_position_notify -}; - -static void -bind_text_cursor_position(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct wl_resource *resource; - - resource = wl_resource_create(client, - &text_cursor_position_interface, 1, id); - if (resource) - wl_resource_set_implementation(resource, - &text_cursor_position_implementation, - data, NULL); -} - -static void -text_cursor_position_notifier_destroy(struct wl_listener *listener, void *data) -{ - struct text_cursor_position *text_cursor_position = - container_of(listener, struct text_cursor_position, destroy_listener); - - wl_global_destroy(text_cursor_position->global); - free(text_cursor_position); -} - -void -text_cursor_position_notifier_create(struct weston_compositor *ec) -{ - struct text_cursor_position *text_cursor_position; - - text_cursor_position = malloc(sizeof *text_cursor_position); - if (text_cursor_position == NULL) - return; - - text_cursor_position->ec = ec; - - text_cursor_position->global = - wl_global_create(ec->wl_display, - &text_cursor_position_interface, 1, - text_cursor_position, - bind_text_cursor_position); - - text_cursor_position->destroy_listener.notify = - text_cursor_position_notifier_destroy; - wl_signal_add(&ec->destroy_signal, &text_cursor_position->destroy_listener); -} - -WL_EXPORT void -weston_text_cursor_position_notify(struct weston_surface *surface, - wl_fixed_t cur_pos_x, - wl_fixed_t cur_pos_y) -{ - struct weston_output *output; - wl_fixed_t global_x, global_y; - - weston_surface_to_global_fixed(surface, cur_pos_x, cur_pos_y, - &global_x, &global_y); - - wl_list_for_each(output, &surface->compositor->output_list, link) - if (output->zoom.active && - pixman_region32_contains_point(&output->region, - wl_fixed_to_int(global_x), - wl_fixed_to_int(global_y), - NULL)) { - output->zoom.text_cursor.x = global_x; - output->zoom.text_cursor.y = global_y; - weston_output_update_zoom(output, ZOOM_FOCUS_TEXT); - } -} - static void weston_zoom_frame_z(struct weston_animation *animation, struct weston_output *output, uint32_t msecs) @@ -136,6 +45,7 @@ weston_zoom_frame_z(struct weston_animation *animation, if (output->zoom.active && output->zoom.level <= 0.0) { output->zoom.active = 0; output->disable_planes--; + wl_list_remove(&output->zoom.motion_listener.link); } output->zoom.spring_z.current = output->zoom.level; wl_list_remove(&animation->link); @@ -176,12 +86,8 @@ weston_zoom_frame_xy(struct weston_animation *animation, if (weston_spring_done(&output->zoom.spring_xy)) { output->zoom.spring_xy.current = output->zoom.spring_xy.target; - output->zoom.current.x = - output->zoom.type == ZOOM_FOCUS_POINTER ? - seat->pointer->x : output->zoom.text_cursor.x; - output->zoom.current.y = - output->zoom.type == ZOOM_FOCUS_POINTER ? - seat->pointer->y : output->zoom.text_cursor.y; + output->zoom.current.x = seat->pointer->x; + output->zoom.current.y = seat->pointer->y; wl_list_remove(&animation->link); wl_list_init(&animation->link); } @@ -251,7 +157,6 @@ weston_zoom_apply_output_transform(struct weston_output *output, static void weston_output_update_zoom_transform(struct weston_output *output) { - uint32_t type = output->zoom.type; float global_x, global_y; wl_fixed_t x = output->zoom.current.x; wl_fixed_t y = output->zoom.current.y; @@ -265,8 +170,7 @@ weston_output_update_zoom_transform(struct weston_output *output) level == 0.0f) return; - if (type == ZOOM_FOCUS_POINTER && - wl_list_empty(&output->zoom.animation_xy.link)) + if (wl_list_empty(&output->zoom.animation_xy.link)) zoom_area_center_from_pointer(output, &x, &y); global_x = wl_fixed_to_double(x); @@ -297,39 +201,8 @@ weston_output_update_zoom_transform(struct weston_output *output) } static void -weston_zoom_transition(struct weston_output *output, uint32_t type, - wl_fixed_t x, wl_fixed_t y) +weston_zoom_transition(struct weston_output *output, wl_fixed_t x, wl_fixed_t y) { - if (output->zoom.type != type) { - /* Set from/to points and start animation */ - output->zoom.spring_xy.current = 0.0; - output->zoom.spring_xy.previous = 0.0; - output->zoom.spring_xy.target = 1.0; - - if (wl_list_empty(&output->zoom.animation_xy.link)) { - output->zoom.animation_xy.frame_counter = 0; - wl_list_insert(output->animation_list.prev, - &output->zoom.animation_xy.link); - - output->zoom.from.x = (type == ZOOM_FOCUS_TEXT) ? - x : output->zoom.text_cursor.x; - output->zoom.from.y = (type == ZOOM_FOCUS_TEXT) ? - y : output->zoom.text_cursor.y; - } else { - output->zoom.from.x = output->zoom.current.x; - output->zoom.from.y = output->zoom.current.y; - } - - output->zoom.to.x = (type == ZOOM_FOCUS_POINTER) ? - x : output->zoom.text_cursor.x; - output->zoom.to.y = (type == ZOOM_FOCUS_POINTER) ? - y : output->zoom.text_cursor.y; - output->zoom.current.x = output->zoom.from.x; - output->zoom.current.y = output->zoom.from.y; - - output->zoom.type = type; - } - if (output->zoom.level != output->zoom.spring_z.current) { output->zoom.spring_z.target = output->zoom.level; if (wl_list_empty(&output->zoom.animation_z.link)) { @@ -344,7 +217,7 @@ weston_zoom_transition(struct weston_output *output, uint32_t type, } WL_EXPORT void -weston_output_update_zoom(struct weston_output *output, uint32_t type) +weston_output_update_zoom(struct weston_output *output) { struct weston_seat *seat = weston_zoom_pick_seat(output->compositor); wl_fixed_t x = seat->pointer->x; @@ -352,30 +225,43 @@ weston_output_update_zoom(struct weston_output *output, uint32_t type) zoom_area_center_from_pointer(output, &x, &y); - if (type == ZOOM_FOCUS_POINTER) { - if (wl_list_empty(&output->zoom.animation_xy.link)) { - output->zoom.current.x = seat->pointer->x; - output->zoom.current.y = seat->pointer->y; - } else { - output->zoom.to.x = x; - output->zoom.to.y = y; - } - } - - if (type == ZOOM_FOCUS_TEXT) { - if (wl_list_empty(&output->zoom.animation_xy.link)) { - output->zoom.current.x = output->zoom.text_cursor.x; - output->zoom.current.y = output->zoom.text_cursor.y; - } else { - output->zoom.to.x = output->zoom.text_cursor.x; - output->zoom.to.y = output->zoom.text_cursor.y; - } + if (wl_list_empty(&output->zoom.animation_xy.link)) { + output->zoom.current.x = seat->pointer->x; + output->zoom.current.y = seat->pointer->y; + } else { + output->zoom.to.x = x; + output->zoom.to.y = y; } - weston_zoom_transition(output, type, x, y); + weston_zoom_transition(output, x, y); weston_output_update_zoom_transform(output); } +static void +motion(struct wl_listener *listener, void *data) +{ + struct weston_output_zoom *zoom = + container_of(listener, struct weston_output_zoom, motion_listener); + struct weston_output *output = + container_of(zoom, struct weston_output, zoom); + + weston_output_update_zoom(output); +} + +WL_EXPORT void +weston_output_activate_zoom(struct weston_output *output) +{ + struct weston_seat *seat = weston_zoom_pick_seat(output->compositor); + + if (output->zoom.active) + return; + + output->zoom.active = 1; + output->disable_planes++; + wl_signal_add(&seat->pointer->motion_signal, + &output->zoom.motion_listener); +} + WL_EXPORT void weston_output_init_zoom(struct weston_output *output) { @@ -385,7 +271,6 @@ weston_output_init_zoom(struct weston_output *output) output->zoom.level = 0.0; output->zoom.trans_x = 0.0; output->zoom.trans_y = 0.0; - output->zoom.type = ZOOM_FOCUS_POINTER; weston_spring_init(&output->zoom.spring_z, 250.0, 0.0, 0.0); output->zoom.spring_z.friction = 1000; output->zoom.animation_z.frame = weston_zoom_frame_z; @@ -394,4 +279,5 @@ weston_output_init_zoom(struct weston_output *output) output->zoom.spring_xy.friction = 1000; output->zoom.animation_xy.frame = weston_zoom_frame_xy; wl_list_init(&output->zoom.animation_xy.link); + output->zoom.motion_listener.notify = motion; } diff --git a/tests/.gitignore b/tests/.gitignore index aba378c3..32a64921 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,3 +1,4 @@ +*.log *.test *.trs *.weston @@ -9,5 +10,3 @@ test-text-client wayland-test-client-protocol.h wayland-test-protocol.c wayland-test-server-protocol.h -subsurface-client-protocol.h -subsurface-protocol.c diff --git a/tests/Makefile.am b/tests/Makefile.am index 5be52c6e..8b851460 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -8,9 +8,8 @@ module_tests = \ surface-test.la \ surface-global-test.la -weston_test = weston-test.la - weston_tests = \ + bad_buffer.weston \ keyboard.weston \ event.weston \ button.weston \ @@ -18,6 +17,11 @@ weston_tests = \ subsurface.weston \ $(xwayland_test) +if ENABLE_EGL +weston_tests += \ + buffer-count.weston +endif + AM_TESTS_ENVIRONMENT = \ abs_builddir='$(abs_builddir)'; export abs_builddir; @@ -32,8 +36,10 @@ clean-local: export abs_builddir noinst_LTLIBRARIES = \ - $(weston_test) \ - $(module_tests) + weston-test.la \ + $(module_tests) \ + libtest-runner.la \ + libtest-client.la noinst_PROGRAMS = \ $(setbacklight) \ @@ -54,8 +60,7 @@ surface_global_test_la_LDFLAGS = -module -avoid-version -rpath $(libdir) surface_test_la_SOURCES = surface-test.c surface_test_la_LDFLAGS = -module -avoid-version -rpath $(libdir) -weston_test_la_LIBADD = $(COMPOSITOR_LIBS) \ - ../shared/libshared.la +weston_test_la_LIBADD = $(COMPOSITOR_LIBS) ../shared/libshared.la weston_test_la_LDFLAGS = -module -avoid-version -rpath $(libdir) weston_test_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) weston_test_la_SOURCES = \ @@ -63,63 +68,66 @@ weston_test_la_SOURCES = \ wayland-test-protocol.c \ wayland-test-server-protocol.h -weston_test_runner_src = \ - weston-test-runner.c \ - weston-test-runner.h - -check_LTLIBRARIES = libshared-test.la +if ENABLE_EGL +weston_test_la_CFLAGS += $(EGL_TESTS_CFLAGS) +weston_test_la_LDFLAGS += $(EGL_TESTS_LIBS) +endif -libshared_test_la_SOURCES = \ - $(weston_test_runner_src) -libshared_test_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) +libtest_runner_la_SOURCES = \ + weston-test-runner.c \ + weston-test-runner.h +libtest_runner_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) config_parser_test_LDADD = \ ../shared/libshared.la \ - libshared-test.la \ + libtest-runner.la \ $(COMPOSITOR_LIBS) config_parser_test_SOURCES = \ config-parser-test.c + vertex_clip_test_SOURCES = \ vertex-clip-test.c \ ../src/vertex-clipping.c \ ../src/vertex-clipping.h vertex_clip_test_LDADD = \ - libshared-test.la \ + libtest-runner.la \ -lm -lrt -weston_test_client_src = \ +libtest_client_la_SOURCES = \ weston-test-client-helper.c \ weston-test-client-helper.h \ wayland-test-protocol.c \ - wayland-test-client-protocol.h \ - subsurface-protocol.c \ - subsurface-client-protocol.h -weston_test_client_libs = \ - $(SIMPLE_CLIENT_LIBS) \ + wayland-test-client-protocol.h +libtest_client_la_LIBADD = \ + $(TEST_CLIENT_LIBS) \ ../shared/libshared.la \ - libshared-test.la + libtest-runner.la -keyboard_weston_SOURCES = keyboard-test.c $(weston_test_client_src) -keyboard_weston_LDADD = $(weston_test_client_libs) +bad_buffer_weston_SOURCES = bad-buffer-test.c +bad_buffer_weston_LDADD = libtest-client.la -event_weston_SOURCES = event-test.c $(weston_test_client_src) -event_weston_LDADD = $(weston_test_client_libs) +keyboard_weston_SOURCES = keyboard-test.c +keyboard_weston_LDADD = libtest-client.la -button_weston_SOURCES = button-test.c $(weston_test_client_src) -button_weston_LDADD = $(weston_test_client_libs) +event_weston_SOURCES = event-test.c +event_weston_LDADD = libtest-client.la -text_weston_SOURCES = \ - text-test.c \ - ../clients/text-protocol.c \ - $(weston_test_client_src) -text_weston_LDADD = $(weston_test_client_libs) +button_weston_SOURCES = button-test.c +button_weston_LDADD = libtest-client.la -subsurface_weston_SOURCES = subsurface-test.c $(weston_test_client_src) -subsurface_weston_LDADD = $(weston_test_client_libs) +text_weston_SOURCES = text-test.c text-protocol.c +text_weston_LDADD = libtest-client.la -xwayland_weston_SOURCES = xwayland-test.c $(weston_test_client_src) +subsurface_weston_SOURCES = subsurface-test.c +subsurface_weston_LDADD = libtest-client.la -xwayland_weston_LDADD = $(weston_test_client_libs) $(XWAYLAND_TEST_LIBS) +buffer_count_weston_SOURCES = buffer-count-test.c +buffer_count_weston_CFLAGS = $(GCC_CFLAGS) $(EGL_TESTS_CFLAGS) +buffer_count_weston_LDADD = libtest-client.la $(EGL_TESTS_LIBS) + +xwayland_weston_SOURCES = xwayland-test.c +xwayland_weston_CFLAGS = $(GCC_CFLAGS) $(XWAYLAND_TEST_CFLAGS) +xwayland_weston_LDADD = libtest-client.la $(XWAYLAND_TEST_LIBS) if ENABLE_XWAYLAND_TEST xwayland_test = xwayland.weston @@ -146,11 +154,11 @@ endif EXTRA_DIST = weston-tests-env BUILT_SOURCES = \ - subsurface-protocol.c \ - subsurface-client-protocol.h \ wayland-test-protocol.c \ wayland-test-server-protocol.h \ - wayland-test-client-protocol.h + wayland-test-client-protocol.h \ + text-protocol.c \ + text-client-protocol.h CLEANFILES = $(BUILT_SOURCES) diff --git a/tests/bad-buffer-test.c b/tests/bad-buffer-test.c new file mode 100644 index 00000000..4f5f8109 --- /dev/null +++ b/tests/bad-buffer-test.c @@ -0,0 +1,77 @@ +/* + * Copyright © 2012 Intel Corporation + * Copyright © 2013 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "../shared/os-compatibility.h" +#include "weston-test-client-helper.h" + +/* tests, that attempt to crash the compositor on purpose */ + +static struct wl_buffer * +create_bad_shm_buffer(struct client *client, int width, int height) +{ + struct wl_shm *shm = client->wl_shm; + int stride = width * 4; + int size = stride * height; + struct wl_shm_pool *pool; + struct wl_buffer *buffer; + int fd; + + fd = os_create_anonymous_file(size); + assert(fd >= 0); + + pool = wl_shm_create_pool(shm, fd, size); + buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, + WL_SHM_FORMAT_ARGB8888); + wl_shm_pool_destroy(pool); + + /* Truncate the file to a small size, so that the compositor + * will access it out-of-bounds, and hit SIGBUS. + */ + assert(ftruncate(fd, 12) == 0); + close(fd); + + return buffer; +} + +FAIL_TEST(test_truncated_shm_file) +{ + struct client *client; + struct wl_buffer *bad_buffer; + struct wl_surface *surface; + int frame; + + client = client_create(46, 76, 111, 134); + assert(client); + surface = client->surface->wl_surface; + + bad_buffer = create_bad_shm_buffer(client, 200, 200); + + wl_surface_attach(surface, bad_buffer, 0, 0); + wl_surface_damage(surface, 0, 0, 200, 200); + frame_callback_set(surface, &frame); + wl_surface_commit(surface); + frame_callback_wait(client, &frame); +} diff --git a/tests/buffer-count-test.c b/tests/buffer-count-test.c new file mode 100644 index 00000000..c7ddeb64 --- /dev/null +++ b/tests/buffer-count-test.c @@ -0,0 +1,141 @@ +/* + * Copyright © 2013 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "weston-test-client-helper.h" +#include +#include +#include + +#include +#include +#include + +struct test_data { + struct client *client; + + EGLDisplay egl_dpy; + EGLContext egl_ctx; + EGLConfig egl_conf; + EGLSurface egl_surface; +}; + +static void +init_egl(struct test_data *test_data) +{ + struct wl_egl_window *native_window; + struct surface *surface = test_data->client->surface; + const char *str, *mesa; + + static const EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_ALPHA_SIZE, 0, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLint major, minor, n; + EGLBoolean ret; + + test_data->egl_dpy = eglGetDisplay((EGLNativeDisplayType) + test_data->client->wl_display); + assert(test_data->egl_dpy); + + ret = eglInitialize(test_data->egl_dpy, &major, &minor); + assert(ret == EGL_TRUE); + ret = eglBindAPI(EGL_OPENGL_ES_API); + assert(ret == EGL_TRUE); + + ret = eglChooseConfig(test_data->egl_dpy, config_attribs, + &test_data->egl_conf, 1, &n); + assert(ret && n == 1); + + test_data->egl_ctx = eglCreateContext(test_data->egl_dpy, + test_data->egl_conf, + EGL_NO_CONTEXT, context_attribs); + assert(test_data->egl_ctx); + + native_window = + wl_egl_window_create(surface->wl_surface, + surface->width, + surface->height); + test_data->egl_surface = + eglCreateWindowSurface(test_data->egl_dpy, + test_data->egl_conf, + (EGLNativeWindowType) native_window, + NULL); + + ret = eglMakeCurrent(test_data->egl_dpy, test_data->egl_surface, + test_data->egl_surface, test_data->egl_ctx); + assert(ret == EGL_TRUE); + + /* This test is specific to mesa 10.1 and later, which is the + * first release that doesn't accidentally triple-buffer. */ + str = (const char *) glGetString(GL_VERSION); + mesa = strstr(str, "Mesa "); + if (mesa == NULL) + skip("unknown EGL implementation (%s)\n", str); + if (sscanf(mesa + 5, "%d.%d", &major, &minor) != 2) + skip("unrecognized mesa version (%s)\n", str); + if (major < 10 || (major == 10 && minor < 1)) + skip("mesa version too old (%s)\n", str); + +} + +TEST(test_buffer_count) +{ + struct test_data test_data; + uint32_t buffer_count; + int i; + + test_data.client = client_create(10, 10, 10, 10); + init_egl(&test_data); + + /* This is meant to represent a typical game loop which is + * expecting eglSwapBuffers to block and throttle the + * rendering to a sensible frame rate. Therefore it doesn't + * expect to have to install a frame callback itself. I'd + * imagine this is what a typical SDL game would end up + * doing */ + + for (i = 0; i < 10; i++) { + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(test_data.egl_dpy, test_data.egl_surface); + } + + buffer_count = get_n_egl_buffers(test_data.client); + + printf("buffers used = %i\n", buffer_count); + + /* The implementation should only end up creating two buffers + * and cycling between them */ + assert(buffer_count == 2); +} diff --git a/tests/subsurface-test.c b/tests/subsurface-test.c index 98e00fea..1c2641b6 100644 --- a/tests/subsurface-test.c +++ b/tests/subsurface-test.c @@ -23,7 +23,6 @@ #include #include "weston-test-client-helper.h" -#include "subsurface-client-protocol.h" #include #define NUM_SUBSURFACES 3 diff --git a/tests/surface-global-test.c b/tests/surface-global-test.c index 04b64d6a..b496635c 100644 --- a/tests/surface-global-test.c +++ b/tests/surface-global-test.c @@ -29,39 +29,44 @@ surface_to_from_global(void *data) { struct weston_compositor *compositor = data; struct weston_surface *surface; + struct weston_view *view; float x, y; wl_fixed_t fx, fy; int32_t ix, iy; surface = weston_surface_create(compositor); assert(surface); - weston_surface_configure(surface, 5, 10, 50, 50); - weston_surface_update_transform(surface); - - weston_surface_to_global_float(surface, 33, 22, &x, &y); + view = weston_view_create(surface); + assert(view); + surface->width = 50; + surface->height = 50; + weston_view_set_position(view, 5, 10); + weston_view_update_transform(view); + + weston_view_to_global_float(view, 33, 22, &x, &y); assert(x == 38 && y == 32); - weston_surface_to_global_float(surface, -8, -2, &x, &y); + weston_view_to_global_float(view, -8, -2, &x, &y); assert(x == -3 && y == 8); - weston_surface_to_global_fixed(surface, wl_fixed_from_int(12), + weston_view_to_global_fixed(view, wl_fixed_from_int(12), wl_fixed_from_int(5), &fx, &fy); assert(fx == wl_fixed_from_int(17) && fy == wl_fixed_from_int(15)); - weston_surface_from_global_float(surface, 38, 32, &x, &y); + weston_view_from_global_float(view, 38, 32, &x, &y); assert(x == 33 && y == 22); - weston_surface_from_global_float(surface, 42, 5, &x, &y); + weston_view_from_global_float(view, 42, 5, &x, &y); assert(x == 37 && y == -5); - weston_surface_from_global_fixed(surface, wl_fixed_from_int(21), + weston_view_from_global_fixed(view, wl_fixed_from_int(21), wl_fixed_from_int(100), &fx, &fy); assert(fx == wl_fixed_from_int(16) && fy == wl_fixed_from_int(90)); - weston_surface_from_global(surface, 0, 0, &ix, &iy); + weston_view_from_global(view, 0, 0, &ix, &iy); assert(ix == -5 && iy == -10); - weston_surface_from_global(surface, 5, 10, &ix, &iy); + weston_view_from_global(view, 5, 10, &ix, &iy); assert(ix == 0 && iy == 0); wl_display_terminate(compositor->wl_display); diff --git a/tests/surface-test.c b/tests/surface-test.c index e8af2ed7..80dce81f 100644 --- a/tests/surface-test.c +++ b/tests/surface-test.c @@ -31,20 +31,25 @@ surface_transform(void *data) { struct weston_compositor *compositor = data; struct weston_surface *surface; + struct weston_view *view; float x, y; surface = weston_surface_create(compositor); assert(surface); - weston_surface_configure(surface, 100, 100, 200, 200); - weston_surface_update_transform(surface); - weston_surface_to_global_float(surface, 20, 20, &x, &y); + view = weston_view_create(surface); + assert(view); + surface->width = 200; + surface->height = 200; + weston_view_set_position(view, 100, 100); + weston_view_update_transform(view); + weston_view_to_global_float(view, 20, 20, &x, &y); fprintf(stderr, "20,20 maps to %f, %f\n", x, y); assert(x == 120 && y == 120); - weston_surface_set_position(surface, 150, 300); - weston_surface_update_transform(surface); - weston_surface_to_global_float(surface, 50, 40, &x, &y); + weston_view_set_position(view, 150, 300); + weston_view_update_transform(view); + weston_view_to_global_float(view, 50, 40, &x, &y); assert(x == 200 && y == 340); wl_display_terminate(compositor->wl_display); diff --git a/tests/text-test.c b/tests/text-test.c index 48f2b5a6..1f10b1bb 100644 --- a/tests/text-test.c +++ b/tests/text-test.c @@ -24,7 +24,7 @@ #include #include #include "weston-test-client-helper.h" -#include "../clients/text-client-protocol.h" +#include "text-client-protocol.h" struct text_input_state { int activated; diff --git a/tests/vertex-clip-test.c b/tests/vertex-clip-test.c index 5b2e08c5..6d44aa2a 100644 --- a/tests/vertex-clip-test.c +++ b/tests/vertex-clip-test.c @@ -56,8 +56,8 @@ populate_clip_context (struct clip_context *ctx) static int clip_polygon (struct clip_context *ctx, struct polygon8 *polygon, - GLfloat *vertices_x, - GLfloat *vertices_y) + float *vertices_x, + float *vertices_y) { populate_clip_context(ctx); return clip_transformed(ctx, polygon, vertices_x, vertices_y); @@ -181,8 +181,8 @@ TEST_P(clip_polygon_n_vertices_emitted, test_data) struct vertex_clip_test_data *tdata = data; struct clip_context ctx; struct polygon8 polygon; - GLfloat vertices_x[8]; - GLfloat vertices_y[8]; + float vertices_x[8]; + float vertices_y[8]; deep_copy_polygon8(&tdata->surface, &polygon); int emitted = clip_polygon(&ctx, &polygon, vertices_x, vertices_y); @@ -194,8 +194,8 @@ TEST_P(clip_polygon_expected_vertices, test_data) struct vertex_clip_test_data *tdata = data; struct clip_context ctx; struct polygon8 polygon; - GLfloat vertices_x[8]; - GLfloat vertices_y[8]; + float vertices_x[8]; + float vertices_y[8]; deep_copy_polygon8(&tdata->surface, &polygon); int emitted = clip_polygon(&ctx, &polygon, vertices_x, vertices_y); int i = 0; diff --git a/tests/weston-test-client-helper.c b/tests/weston-test-client-helper.c index b19be400..399aa443 100644 --- a/tests/weston-test-client-helper.c +++ b/tests/weston-test-client-helper.c @@ -111,6 +111,17 @@ move_client(struct client *client, int x, int y) frame_callback_wait(client, &done); } +int +get_n_egl_buffers(struct client *client) +{ + client->test->n_egl_buffers = -1; + + wl_test_get_n_egl_buffers(client->test->wl_test); + wl_display_roundtrip(client->wl_display); + + return client->test->n_egl_buffers; +} + static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *wl_surface, @@ -340,8 +351,17 @@ test_handle_pointer_position(void *data, struct wl_test *wl_test, test->pointer_x, test->pointer_y); } +static void +test_handle_n_egl_buffers(void *data, struct wl_test *wl_test, uint32_t n) +{ + struct test *test = data; + + test->n_egl_buffers = n; +} + static const struct wl_test_listener test_listener = { - test_handle_pointer_position + test_handle_pointer_position, + test_handle_n_egl_buffers, }; static void @@ -476,6 +496,20 @@ static const struct wl_registry_listener registry_listener = { handle_global }; +void +skip(const char *fmt, ...) +{ + va_list argp; + + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + + /* automake tests uses exit code 77, but we don't have a good + * way to make weston exit with that from here. */ + exit(0); +} + static void log_handler(const char *fmt, va_list args) { diff --git a/tests/weston-test-client-helper.h b/tests/weston-test-client-helper.h index a5edca90..6670ab37 100644 --- a/tests/weston-test-client-helper.h +++ b/tests/weston-test-client-helper.h @@ -51,6 +51,7 @@ struct test { struct wl_test *wl_test; int pointer_x; int pointer_y; + uint32_t n_egl_buffers; }; struct input { @@ -120,4 +121,10 @@ frame_callback_set(struct wl_surface *surface, int *done); void frame_callback_wait(struct client *client, int *done); +int +get_n_egl_buffers(struct client *client); + +void +skip(const char *fmt, ...); + #endif diff --git a/tests/weston-test.c b/tests/weston-test.c index bc5b6e9d..844059dc 100644 --- a/tests/weston-test.c +++ b/tests/weston-test.c @@ -20,6 +20,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include #include #include @@ -28,6 +30,11 @@ #include "../src/compositor.h" #include "wayland-test-server-protocol.h" +#ifdef ENABLE_EGL +#include +#include +#endif /* ENABLE_EGL */ + struct weston_test { struct weston_compositor *compositor; struct weston_layer layer; @@ -36,6 +43,7 @@ struct weston_test { struct weston_test_surface { struct weston_surface *surface; + struct weston_view *view; int32_t x, y; struct weston_test *test; }; @@ -74,20 +82,19 @@ notify_pointer_position(struct weston_test *test, struct wl_resource *resource) } static void -test_surface_configure(struct weston_surface *surface, int32_t sx, int32_t sy, int32_t width, int32_t height) +test_surface_configure(struct weston_surface *surface, int32_t sx, int32_t sy) { struct weston_test_surface *test_surface = surface->configure_private; struct weston_test *test = test_surface->test; - if (wl_list_empty(&surface->layer_link)) - wl_list_insert(&test->layer.surface_list, - &surface->layer_link); + if (wl_list_empty(&test_surface->view->layer_link)) + wl_list_insert(&test->layer.view_list, + &test_surface->view->layer_link); - weston_surface_configure(surface, test_surface->x, test_surface->y, - width, height); + weston_view_set_position(test_surface->view, + test_surface->x, test_surface->y); - if (!weston_surface_is_mapped(surface)) - weston_surface_update_transform(surface); + weston_view_update_transform(test_surface->view); } static void @@ -99,13 +106,23 @@ move_surface(struct wl_client *client, struct wl_resource *resource, wl_resource_get_user_data(surface_resource); struct weston_test_surface *test_surface; - surface->configure = test_surface_configure; - if (surface->configure_private == NULL) - surface->configure_private = malloc(sizeof *test_surface); test_surface = surface->configure_private; - if (test_surface == NULL) { - wl_resource_post_no_memory(resource); - return; + if (!test_surface) { + test_surface = malloc(sizeof *test_surface); + if (!test_surface) { + wl_resource_post_no_memory(resource); + return; + } + + test_surface->view = weston_view_create(surface); + if (!test_surface->view) { + wl_resource_post_no_memory(resource); + free(test_surface); + return; + } + + surface->configure_private = test_surface; + surface->configure = test_surface_configure; } test_surface->surface = surface; @@ -122,8 +139,6 @@ move_pointer(struct wl_client *client, struct wl_resource *resource, struct weston_seat *seat = get_seat(test); struct weston_pointer *pointer = seat->pointer; - test->compositor->focus = 1; - notify_motion(seat, 100, wl_fixed_from_int(x) - pointer->x, wl_fixed_from_int(y) - pointer->y); @@ -138,8 +153,6 @@ send_button(struct wl_client *client, struct wl_resource *resource, struct weston_test *test = wl_resource_get_user_data(resource); struct weston_seat *seat = get_seat(test); - test->compositor->focus = 1; - notify_button(seat, 100, button, state); } @@ -172,17 +185,57 @@ send_key(struct wl_client *client, struct wl_resource *resource, struct weston_test *test = wl_resource_get_user_data(resource); struct weston_seat *seat = get_seat(test); - test->compositor->focus = 1; - notify_key(seat, 100, key, state, STATE_UPDATE_AUTOMATIC); } +#ifdef ENABLE_EGL +static int +is_egl_buffer(struct wl_resource *resource) +{ + PFNEGLQUERYWAYLANDBUFFERWL query_buffer = + (void *) eglGetProcAddress("eglQueryWaylandBufferWL"); + EGLint format; + + if (query_buffer(eglGetCurrentDisplay(), + resource, + EGL_TEXTURE_FORMAT, + &format)) + return 1; + + return 0; +} +#endif /* ENABLE_EGL */ + +static void +get_n_buffers(struct wl_client *client, struct wl_resource *resource) +{ + int n_buffers = 0; + +#ifdef ENABLE_EGL + struct wl_resource *buffer_resource; + int i; + + for (i = 0; i < 1000; i++) { + buffer_resource = wl_client_get_object(client, i); + + if (buffer_resource == NULL) + continue; + + if (is_egl_buffer(buffer_resource)) + n_buffers++; + } +#endif /* ENABLE_EGL */ + + wl_test_send_n_egl_buffers(resource, n_buffers); +} + static const struct wl_test_interface test_implementation = { move_surface, move_pointer, send_button, activate_surface, - send_key + send_key, + get_n_buffers, }; static void @@ -208,7 +261,7 @@ idle_launch_client(void *data) path = getenv("WESTON_TEST_CLIENT_PATH"); if (path == NULL) - exit(EXIT_FAILURE); + return; pid = fork(); if (pid == -1) exit(EXIT_FAILURE); diff --git a/weston.ini b/weston.ini deleted file mode 100644 index 70069316..00000000 --- a/weston.ini +++ /dev/null @@ -1,66 +0,0 @@ -[core] -#modules=xwayland.so,cms-colord.so -#shell=desktop-shell.so - -[shell] -background-image=/usr/share/backgrounds/gnome/Aqua.jpg -background-color=0xff002244 -background-type=tile -panel-color=0x90ff0000 -locking=true -animation=zoom -startup-animation=fade -#binding-modifier=ctrl -#num-workspaces=6 -#cursor-theme=whiteglass -#cursor-size=24 - -#lockscreen-icon=/usr/share/icons/gnome/256x256/actions/lock.png -#lockscreen=/usr/share/backgrounds/gnome/Garden.jpg -#homescreen=/usr/share/backgrounds/gnome/Blinds.jpg -#animation=fade - -[launcher] -icon=/usr/share/icons/gnome/24x24/apps/utilities-terminal.png -path=/usr/bin/gnome-terminal - -[launcher] -icon=/usr/share/icons/gnome/24x24/apps/utilities-terminal.png -path=/usr/bin/weston-terminal - -[launcher] -icon=/usr/share/icons/hicolor/24x24/apps/google-chrome.png -path=/usr/bin/google-chrome - -[launcher] -icon=/usr/share/icons/gnome/24x24/apps/arts.png -path=./clients/weston-flower - -[screensaver] -# Uncomment path to disable screensaver -path=/usr/libexec/weston-screensaver -duration=600 - -[input-method] -path=/usr/libexec/weston-keyboard - -#[output] -#name=LVDS1 -#mode=1680x1050 -#transform=90 -#icc_profile=/usr/share/color/icc/colord/Bluish.icc - -#[output] -#name=VGA1 -#mode=173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync -#transform=flipped - -#[output] -#name=X1 -#mode=1024x768 -#transform=flipped-270 - -#[touchpad] -#constant_accel_factor = 50 -#min_accel_factor = 0.16 -#max_accel_factor = 1.0 diff --git a/weston.ini.in b/weston.ini.in new file mode 100644 index 00000000..5181a9e5 --- /dev/null +++ b/weston.ini.in @@ -0,0 +1,67 @@ +[core] +#modules=xwayland.so,cms-colord.so +#shell=desktop-shell.so +#gbm-format=xrgb2101010 + +[shell] +background-image=/usr/share/backgrounds/gnome/Aqua.jpg +background-color=0xff002244 +background-type=tile +panel-color=0x90ff0000 +locking=true +animation=zoom +startup-animation=fade +#binding-modifier=ctrl +#num-workspaces=6 +#cursor-theme=whiteglass +#cursor-size=24 + +#lockscreen-icon=/usr/share/icons/gnome/256x256/actions/lock.png +#lockscreen=/usr/share/backgrounds/gnome/Garden.jpg +#homescreen=/usr/share/backgrounds/gnome/Blinds.jpg +#animation=fade + +[launcher] +icon=/usr/share/icons/gnome/24x24/apps/utilities-terminal.png +path=/usr/bin/gnome-terminal + +[launcher] +icon=/usr/share/icons/gnome/24x24/apps/utilities-terminal.png +path=@bindir@/weston-terminal + +[launcher] +icon=/usr/share/icons/hicolor/24x24/apps/google-chrome.png +path=/usr/bin/google-chrome + +[launcher] +icon=/usr/share/icons/gnome/24x24/apps/arts.png +path=@abs_top_builddir@/clients/weston-flower + +[screensaver] +# Uncomment path to disable screensaver +path=@libexecdir@/weston-screensaver +duration=600 + +[input-method] +path=@libexecdir@/weston-keyboard + +#[output] +#name=LVDS1 +#mode=1680x1050 +#transform=90 +#icc_profile=/usr/share/color/icc/colord/Bluish.icc + +#[output] +#name=VGA1 +#mode=173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync +#transform=flipped + +#[output] +#name=X1 +#mode=1024x768 +#transform=flipped-270 + +#[touchpad] +#constant_accel_factor = 50 +#min_accel_factor = 0.16 +#max_accel_factor = 1.0 diff --git a/xwayland/.gitignore b/xwayland/.gitignore new file mode 100644 index 00000000..2c11e7b0 --- /dev/null +++ b/xwayland/.gitignore @@ -0,0 +1,2 @@ +xserver-protocol.c +xserver-server-protocol.h diff --git a/xwayland/Makefile.am b/xwayland/Makefile.am new file mode 100644 index 00000000..434234aa --- /dev/null +++ b/xwayland/Makefile.am @@ -0,0 +1,42 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/shared \ + -I$(top_srcdir)/src \ + -I$(top_builddir)/src \ + -I$(top_builddir)/xwayland \ + -DDATADIR='"$(datadir)"' \ + -DMODULEDIR='"$(moduledir)"' \ + -DLIBEXECDIR='"$(libexecdir)"' \ + -DXSERVER_PATH='"@XSERVER_PATH@"' + +moduledir = @libdir@/weston +module_LTLIBRARIES = xwayland.la + +xwayland = xwayland.la +xwayland_la_LDFLAGS = -module -avoid-version +xwayland_la_LIBADD = \ + $(XWAYLAND_LIBS) \ + $(top_builddir)/shared/libshared-cairo.la +xwayland_la_CFLAGS = \ + $(GCC_CFLAGS) \ + $(COMPOSITOR_CFLAGS) \ + $(PIXMAN_CFLAGS) \ + $(CAIRO_CFLAGS) +xwayland_la_SOURCES = \ + xwayland.h \ + window-manager.c \ + selection.c \ + dnd.c \ + launcher.c \ + xserver-protocol.c \ + xserver-server-protocol.h \ + hash.c \ + hash.h + +BUILT_SOURCES = \ + xserver-protocol.c \ + xserver-server-protocol.h + +CLEANFILES = $(BUILT_SOURCES) + +wayland_protocoldir = $(top_srcdir)/protocol +include $(top_srcdir)/wayland-scanner.mk diff --git a/xwayland/dnd.c b/xwayland/dnd.c new file mode 100644 index 00000000..daeb08de --- /dev/null +++ b/xwayland/dnd.c @@ -0,0 +1,274 @@ +/* + * Copyright © 2013 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xwayland.h" + +#include "cairo-util.h" +#include "compositor.h" +#include "xserver-server-protocol.h" +#include "hash.h" + +static void +weston_dnd_start(struct weston_wm *wm, xcb_window_t owner) +{ + uint32_t values[1], version = 4; + + wm->dnd_window = xcb_generate_id(wm->conn); + values[0] = + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | + XCB_EVENT_MASK_PROPERTY_CHANGE; + + xcb_create_window(wm->conn, + XCB_COPY_FROM_PARENT, + wm->dnd_window, + wm->screen->root, + 0, 0, + 8192, 8192, + 0, + XCB_WINDOW_CLASS_INPUT_ONLY, + wm->screen->root_visual, + XCB_CW_EVENT_MASK, values); + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + wm->dnd_window, + wm->atom.xdnd_aware, + XCB_ATOM_ATOM, + 32, /* format */ + 1, &version); + + xcb_map_window(wm->conn, wm->dnd_window); + wm->dnd_owner = owner; +} + +static void +weston_dnd_stop(struct weston_wm *wm) +{ + xcb_destroy_window(wm->conn, wm->dnd_window); + wm->dnd_window = XCB_WINDOW_NONE; +} + +struct dnd_data_source { + struct weston_data_source base; + struct weston_wm *wm; + int version; + uint32_t window; +}; + +static void +data_source_accept(struct weston_data_source *base, + uint32_t time, const char *mime_type) +{ + struct dnd_data_source *source = (struct dnd_data_source *) base; + xcb_client_message_event_t client_message; + struct weston_wm *wm = source->wm; + + weston_log("got accept, mime-type %s\n", mime_type); + + /* FIXME: If we rewrote UTF8_STRING to + * text/plain;charset=utf-8 and the source doesn't support the + * mime-type, we'll have to rewrite the mime-type back to + * UTF8_STRING here. */ + + client_message.response_type = XCB_CLIENT_MESSAGE; + client_message.format = 32; + client_message.window = wm->dnd_window; + client_message.type = wm->atom.xdnd_status; + client_message.data.data32[0] = wm->dnd_window; + client_message.data.data32[1] = 2; + if (mime_type) + client_message.data.data32[1] |= 1; + client_message.data.data32[2] = 0; + client_message.data.data32[3] = 0; + client_message.data.data32[4] = wm->atom.xdnd_action_copy; + + xcb_send_event(wm->conn, 0, wm->dnd_owner, + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + (char *) &client_message); +} + +static void +data_source_send(struct weston_data_source *base, + const char *mime_type, int32_t fd) +{ + struct dnd_data_source *source = (struct dnd_data_source *) base; + struct weston_wm *wm = source->wm; + + weston_log("got send, %s\n", mime_type); + + /* Get data for the utf8_string target */ + xcb_convert_selection(wm->conn, + wm->selection_window, + wm->atom.xdnd_selection, + wm->atom.utf8_string, + wm->atom.wl_selection, + XCB_TIME_CURRENT_TIME); + + xcb_flush(wm->conn); + + fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK); + wm->data_source_fd = fd; +} + +static void +data_source_cancel(struct weston_data_source *source) +{ + weston_log("got cancel\n"); +} + +static void +handle_enter(struct weston_wm *wm, xcb_client_message_event_t *client_message) +{ + struct dnd_data_source *source; + struct weston_seat *seat = weston_wm_pick_seat(wm); + char **p; + const char *name; + uint32_t *types; + int i, length, has_text; + xcb_get_property_cookie_t cookie; + xcb_get_property_reply_t *reply; + + source = malloc(sizeof *source); + if (source == NULL) + return; + + wl_signal_init(&source->base.destroy_signal); + source->base.accept = data_source_accept; + source->base.send = data_source_send; + source->base.cancel = data_source_cancel; + source->wm = wm; + source->window = client_message->data.data32[0]; + source->version = client_message->data.data32[1] >> 24; + + if (client_message->data.data32[1] & 1) { + cookie = xcb_get_property(wm->conn, + 0, /* delete */ + source->window, + wm->atom.xdnd_type_list, + XCB_ATOM_ANY, 0, 2048); + reply = xcb_get_property_reply(wm->conn, cookie, NULL); + types = xcb_get_property_value(reply); + length = reply->value_len; + } else { + reply = NULL; + types = &client_message->data.data32[2]; + length = 3; + } + + wl_array_init(&source->base.mime_types); + has_text = 0; + for (i = 0; i < length; i++) { + if (types[i] == XCB_ATOM_NONE) + continue; + + name = get_atom_name(wm->conn, types[i]); + if (types[i] == wm->atom.utf8_string || + types[i] == wm->atom.text_plain_utf8 || + types[i] == wm->atom.text_plain) { + if (has_text) + continue; + + has_text = 1; + p = wl_array_add(&source->base.mime_types, sizeof *p); + if (p) + *p = strdup("text/plain;charset=utf-8"); + } else if (strchr(name, '/')) { + p = wl_array_add(&source->base.mime_types, sizeof *p); + if (p) + *p = strdup(name); + } + } + + free(reply); + weston_pointer_start_drag(seat->pointer, &source->base, NULL, NULL); +} + +int +weston_wm_handle_dnd_event(struct weston_wm *wm, + xcb_generic_event_t *event) +{ + xcb_xfixes_selection_notify_event_t *xfixes_selection_notify = + (xcb_xfixes_selection_notify_event_t *) event; + xcb_client_message_event_t *client_message = + (xcb_client_message_event_t *) event; + + switch (event->response_type - wm->xfixes->first_event) { + case XCB_XFIXES_SELECTION_NOTIFY: + if (xfixes_selection_notify->selection != wm->atom.xdnd_selection) + return 0; + + weston_log("XdndSelection owner: %d!\n", + xfixes_selection_notify->owner); + + if (xfixes_selection_notify->owner != XCB_WINDOW_NONE) + weston_dnd_start(wm, xfixes_selection_notify->owner); + else + weston_dnd_stop(wm); + + return 1; + } + + switch (EVENT_TYPE(event)) { + case XCB_CLIENT_MESSAGE: + if (client_message->type == wm->atom.xdnd_enter) { + handle_enter(wm, client_message); + return 1; + } else if (client_message->type == wm->atom.xdnd_leave) { + weston_log("got leave!\n"); + return 1; + } else if (client_message->type == wm->atom.xdnd_drop) { + weston_log("got drop!\n"); + return 1; + } else if (client_message->type == wm->atom.xdnd_drop) { + weston_log("got enter!\n"); + return 1; + } + return 0; + } + + return 0; +} + +void +weston_wm_dnd_init(struct weston_wm *wm) +{ + uint32_t mask; + + mask = + XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | + XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | + XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; + xcb_xfixes_select_selection_input(wm->conn, wm->selection_window, + wm->atom.xdnd_selection, mask); +} diff --git a/xwayland/hash.c b/xwayland/hash.c new file mode 100644 index 00000000..54f3de93 --- /dev/null +++ b/xwayland/hash.c @@ -0,0 +1,309 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1988-2004 Keith Packard and Bart Massey. + * + * 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 (including the next + * paragraph) 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. + * + * Except as contained in this notice, the names of the authors + * or their institutions shall not be used in advertising or + * otherwise to promote the sale, use or other dealings in this + * Software without prior written authorization from the + * authors. + * + * Authors: + * Eric Anholt + * Keith Packard + */ + +#include + +#include +#include + +#include "hash.h" + +struct hash_entry { + uint32_t hash; + void *data; +}; + +struct hash_table { + struct hash_entry *table; + uint32_t size; + uint32_t rehash; + uint32_t max_entries; + uint32_t size_index; + uint32_t entries; + uint32_t deleted_entries; +}; + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +/* + * From Knuth -- a good choice for hash/rehash values is p, p-2 where + * p and p-2 are both prime. These tables are sized to have an extra 10% + * free to avoid exponential performance degradation as the hash table fills + */ + +static const uint32_t deleted_data; + +static const struct { + uint32_t max_entries, size, rehash; +} hash_sizes[] = { + { 2, 5, 3 }, + { 4, 7, 5 }, + { 8, 13, 11 }, + { 16, 19, 17 }, + { 32, 43, 41 }, + { 64, 73, 71 }, + { 128, 151, 149 }, + { 256, 283, 281 }, + { 512, 571, 569 }, + { 1024, 1153, 1151 }, + { 2048, 2269, 2267 }, + { 4096, 4519, 4517 }, + { 8192, 9013, 9011 }, + { 16384, 18043, 18041 }, + { 32768, 36109, 36107 }, + { 65536, 72091, 72089 }, + { 131072, 144409, 144407 }, + { 262144, 288361, 288359 }, + { 524288, 576883, 576881 }, + { 1048576, 1153459, 1153457 }, + { 2097152, 2307163, 2307161 }, + { 4194304, 4613893, 4613891 }, + { 8388608, 9227641, 9227639 }, + { 16777216, 18455029, 18455027 }, + { 33554432, 36911011, 36911009 }, + { 67108864, 73819861, 73819859 }, + { 134217728, 147639589, 147639587 }, + { 268435456, 295279081, 295279079 }, + { 536870912, 590559793, 590559791 }, + { 1073741824, 1181116273, 1181116271}, + { 2147483648ul, 2362232233ul, 2362232231ul} +}; + +static int +entry_is_free(struct hash_entry *entry) +{ + return entry->data == NULL; +} + +static int +entry_is_deleted(struct hash_entry *entry) +{ + return entry->data == &deleted_data; +} + +static int +entry_is_present(struct hash_entry *entry) +{ + return entry->data != NULL && entry->data != &deleted_data; +} + +struct hash_table * +hash_table_create(void) +{ + struct hash_table *ht; + + ht = malloc(sizeof(*ht)); + if (ht == NULL) + return NULL; + + ht->size_index = 0; + ht->size = hash_sizes[ht->size_index].size; + ht->rehash = hash_sizes[ht->size_index].rehash; + ht->max_entries = hash_sizes[ht->size_index].max_entries; + ht->table = calloc(ht->size, sizeof(*ht->table)); + ht->entries = 0; + ht->deleted_entries = 0; + + if (ht->table == NULL) { + free(ht); + return NULL; + } + + return ht; +} + +/** + * Frees the given hash table. + */ +void +hash_table_destroy(struct hash_table *ht) +{ + if (!ht) + return; + + free(ht->table); + free(ht); +} + +/** + * Finds a hash table entry with the given key and hash of that key. + * + * Returns NULL if no entry is found. Note that the data pointer may be + * modified by the user. + */ +static void * +hash_table_search(struct hash_table *ht, uint32_t hash) +{ + uint32_t hash_address; + + hash_address = hash % ht->size; + do { + uint32_t double_hash; + + struct hash_entry *entry = ht->table + hash_address; + + if (entry_is_free(entry)) { + return NULL; + } else if (entry_is_present(entry) && entry->hash == hash) { + return entry; + } + + double_hash = 1 + hash % ht->rehash; + + hash_address = (hash_address + double_hash) % ht->size; + } while (hash_address != hash % ht->size); + + return NULL; +} + +void +hash_table_for_each(struct hash_table *ht, + hash_table_iterator_func_t func, void *data) +{ + struct hash_entry *entry; + uint32_t i; + + for (i = 0; i < ht->size; i++) { + entry = ht->table + i; + if (entry_is_present(entry)) + func(entry->data, data); + } +} + +void * +hash_table_lookup(struct hash_table *ht, uint32_t hash) +{ + struct hash_entry *entry; + + entry = hash_table_search(ht, hash); + if (entry != NULL) + return entry->data; + + return NULL; +} + +static void +hash_table_rehash(struct hash_table *ht, unsigned int new_size_index) +{ + struct hash_table old_ht; + struct hash_entry *table, *entry; + + if (new_size_index >= ARRAY_SIZE(hash_sizes)) + return; + + table = calloc(hash_sizes[new_size_index].size, sizeof(*ht->table)); + if (table == NULL) + return; + + old_ht = *ht; + + ht->table = table; + ht->size_index = new_size_index; + ht->size = hash_sizes[ht->size_index].size; + ht->rehash = hash_sizes[ht->size_index].rehash; + ht->max_entries = hash_sizes[ht->size_index].max_entries; + ht->entries = 0; + ht->deleted_entries = 0; + + for (entry = old_ht.table; + entry != old_ht.table + old_ht.size; + entry++) { + if (entry_is_present(entry)) { + hash_table_insert(ht, entry->hash, entry->data); + } + } + + free(old_ht.table); +} + +/** + * Inserts the data with the given hash into the table. + * + * Note that insertion may rearrange the table on a resize or rehash, + * so previously found hash_entries are no longer valid after this function. + */ +int +hash_table_insert(struct hash_table *ht, uint32_t hash, void *data) +{ + uint32_t hash_address; + + if (ht->entries >= ht->max_entries) { + hash_table_rehash(ht, ht->size_index + 1); + } else if (ht->deleted_entries + ht->entries >= ht->max_entries) { + hash_table_rehash(ht, ht->size_index); + } + + hash_address = hash % ht->size; + do { + struct hash_entry *entry = ht->table + hash_address; + uint32_t double_hash; + + if (!entry_is_present(entry)) { + if (entry_is_deleted(entry)) + ht->deleted_entries--; + entry->hash = hash; + entry->data = data; + ht->entries++; + return 0; + } + + double_hash = 1 + hash % ht->rehash; + + hash_address = (hash_address + double_hash) % ht->size; + } while (hash_address != hash % ht->size); + + /* We could hit here if a required resize failed. An unchecked-malloc + * application could ignore this result. + */ + return -1; +} + +/** + * This function deletes the given hash table entry. + * + * Note that deletion doesn't otherwise modify the table, so an iteration over + * the table deleting entries is safe. + */ +void +hash_table_remove(struct hash_table *ht, uint32_t hash) +{ + struct hash_entry *entry; + + entry = hash_table_search(ht, hash); + if (entry != NULL) { + entry->data = (void *) &deleted_data; + ht->entries--; + ht->deleted_entries++; + } +} diff --git a/xwayland/hash.h b/xwayland/hash.h new file mode 100644 index 00000000..6e1674e1 --- /dev/null +++ b/xwayland/hash.h @@ -0,0 +1,49 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1988-2004 Keith Packard and Bart Massey. + * + * 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 (including the next + * paragraph) 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. + * + * Except as contained in this notice, the names of the authors + * or their institutions shall not be used in advertising or + * otherwise to promote the sale, use or other dealings in this + * Software without prior written authorization from the + * authors. + * + * Authors: + * Eric Anholt + * Keith Packard + */ + +#ifndef HASH_H +#define HASH_H + +struct hash_table; +struct hash_table *hash_table_create(void); +typedef void (*hash_table_iterator_func_t)(void *element, void *data); + +void hash_table_destroy(struct hash_table *ht); +void *hash_table_lookup(struct hash_table *ht, uint32_t hash); +int hash_table_insert(struct hash_table *ht, uint32_t hash, void *data); +void hash_table_remove(struct hash_table *ht, uint32_t hash); +void hash_table_for_each(struct hash_table *ht, + hash_table_iterator_func_t func, void *data); + +#endif diff --git a/xwayland/launcher.c b/xwayland/launcher.c new file mode 100644 index 00000000..8d8e060a --- /dev/null +++ b/xwayland/launcher.c @@ -0,0 +1,389 @@ +/* + * Copyright © 2011 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xwayland.h" +#include "xserver-server-protocol.h" + + +static int +weston_xserver_handle_event(int listen_fd, uint32_t mask, void *data) +{ + struct weston_xserver *wxs = data; + char display[8], s[8]; + int sv[2], client_fd; + char *xserver = NULL; + struct weston_config_section *section; + + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { + weston_log("socketpair failed\n"); + return 1; + } + + wxs->process.pid = fork(); + switch (wxs->process.pid) { + case 0: + /* SOCK_CLOEXEC closes both ends, so we need to unset + * the flag on the client fd. */ + client_fd = dup(sv[1]); + if (client_fd < 0) + return 1; + + snprintf(s, sizeof s, "%d", client_fd); + setenv("WAYLAND_SOCKET", s, 1); + + snprintf(display, sizeof display, ":%d", wxs->display); + + section = weston_config_get_section(wxs->compositor->config, "xwayland", NULL, NULL); + weston_config_section_get_string(section, "path", &xserver, XSERVER_PATH); + + if (execl(xserver, + xserver, + display, + "-wayland", + "-rootless", + "-retro", + "-nolisten", "all", + "-terminate", + NULL) < 0) + weston_log("exec failed: %m\n"); + free(xserver); + _exit(EXIT_FAILURE); + + default: + weston_log("forked X server, pid %d\n", wxs->process.pid); + + close(sv[1]); + wxs->client = wl_client_create(wxs->wl_display, sv[0]); + + weston_watch_process(&wxs->process); + + wl_event_source_remove(wxs->abstract_source); + wl_event_source_remove(wxs->unix_source); + break; + + case -1: + weston_log( "failed to fork\n"); + break; + } + + return 1; +} + +static void +weston_xserver_shutdown(struct weston_xserver *wxs) +{ + char path[256]; + + snprintf(path, sizeof path, "/tmp/.X%d-lock", wxs->display); + unlink(path); + snprintf(path, sizeof path, "/tmp/.X11-unix/X%d", wxs->display); + unlink(path); + if (wxs->process.pid == 0) { + wl_event_source_remove(wxs->abstract_source); + wl_event_source_remove(wxs->unix_source); + } + close(wxs->abstract_fd); + close(wxs->unix_fd); + if (wxs->wm) + weston_wm_destroy(wxs->wm); + wxs->loop = NULL; +} + +static void +weston_xserver_cleanup(struct weston_process *process, int status) +{ + struct weston_xserver *wxs = + container_of(process, struct weston_xserver, process); + + wxs->process.pid = 0; + wxs->client = NULL; + wxs->resource = NULL; + + wxs->abstract_source = + wl_event_loop_add_fd(wxs->loop, wxs->abstract_fd, + WL_EVENT_READABLE, + weston_xserver_handle_event, wxs); + + wxs->unix_source = + wl_event_loop_add_fd(wxs->loop, wxs->unix_fd, + WL_EVENT_READABLE, + weston_xserver_handle_event, wxs); + + if (wxs->wm) { + weston_log("xserver exited, code %d\n", status); + weston_wm_destroy(wxs->wm); + wxs->wm = NULL; + } else { + /* If the X server crashes before it binds to the + * xserver interface, shut down and don't try + * again. */ + weston_log("xserver crashing too fast: %d\n", status); + weston_xserver_shutdown(wxs); + } +} + +static void +bind_xserver(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct weston_xserver *wxs = data; + + /* If it's a different client than the xserver we launched, + * don't start the wm. */ + if (client != wxs->client) + return; + + wxs->resource = + wl_resource_create(client, &xserver_interface, + 1, id); + wl_resource_set_implementation(wxs->resource, &xserver_implementation, + wxs, NULL); + + wxs->wm = weston_wm_create(wxs); + if (wxs->wm == NULL) { + weston_log("failed to create wm\n"); + } + + xserver_send_listen_socket(wxs->resource, wxs->abstract_fd); + xserver_send_listen_socket(wxs->resource, wxs->unix_fd); +} + +static int +bind_to_abstract_socket(int display) +{ + struct sockaddr_un addr; + socklen_t size, name_size; + int fd; + + fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) + return -1; + + addr.sun_family = AF_LOCAL; + name_size = snprintf(addr.sun_path, sizeof addr.sun_path, + "%c/tmp/.X11-unix/X%d", 0, display); + size = offsetof(struct sockaddr_un, sun_path) + name_size; + if (bind(fd, (struct sockaddr *) &addr, size) < 0) { + weston_log("failed to bind to @%s: %s\n", + addr.sun_path + 1, strerror(errno)); + close(fd); + return -1; + } + + if (listen(fd, 1) < 0) { + close(fd); + return -1; + } + + return fd; +} + +static int +bind_to_unix_socket(int display) +{ + struct sockaddr_un addr; + socklen_t size, name_size; + int fd; + + fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd < 0) + return -1; + + addr.sun_family = AF_LOCAL; + name_size = snprintf(addr.sun_path, sizeof addr.sun_path, + "/tmp/.X11-unix/X%d", display) + 1; + size = offsetof(struct sockaddr_un, sun_path) + name_size; + unlink(addr.sun_path); + if (bind(fd, (struct sockaddr *) &addr, size) < 0) { + weston_log("failed to bind to %s (%s)\n", + addr.sun_path, strerror(errno)); + close(fd); + return -1; + } + + if (listen(fd, 1) < 0) { + unlink(addr.sun_path); + close(fd); + return -1; + } + + return fd; +} + +static int +create_lockfile(int display, char *lockfile, size_t lsize) +{ + char pid[16], *end; + int fd, size; + pid_t other; + + snprintf(lockfile, lsize, "/tmp/.X%d-lock", display); + fd = open(lockfile, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444); + if (fd < 0 && errno == EEXIST) { + fd = open(lockfile, O_CLOEXEC, O_RDONLY); + if (fd < 0 || read(fd, pid, 11) != 11) { + weston_log("can't read lock file %s: %s\n", + lockfile, strerror(errno)); + if (fd >= 0) + close (fd); + + errno = EEXIST; + return -1; + } + + other = strtol(pid, &end, 0); + if (end != pid + 10) { + weston_log("can't parse lock file %s\n", + lockfile); + close(fd); + errno = EEXIST; + return -1; + } + + if (kill(other, 0) < 0 && errno == ESRCH) { + /* stale lock file; unlink and try again */ + weston_log("unlinking stale lock file %s\n", lockfile); + close(fd); + if (unlink(lockfile)) + /* If we fail to unlink, return EEXIST + so we try the next display number.*/ + errno = EEXIST; + else + errno = EAGAIN; + return -1; + } + + close(fd); + errno = EEXIST; + return -1; + } else if (fd < 0) { + weston_log("failed to create lock file %s: %s\n", + lockfile, strerror(errno)); + return -1; + } + + /* Subtle detail: we use the pid of the wayland + * compositor, not the xserver in the lock file. */ + size = snprintf(pid, sizeof pid, "%10d\n", getpid()); + if (write(fd, pid, size) != size) { + unlink(lockfile); + close(fd); + return -1; + } + + close(fd); + + return 0; +} + +static void +weston_xserver_destroy(struct wl_listener *l, void *data) +{ + struct weston_xserver *wxs = + container_of(l, struct weston_xserver, destroy_listener); + + if (!wxs) + return; + + if (wxs->loop) + weston_xserver_shutdown(wxs); + + free(wxs); +} + +WL_EXPORT int +module_init(struct weston_compositor *compositor, + int *argc, char *argv[]) + +{ + struct wl_display *display = compositor->wl_display; + struct weston_xserver *wxs; + char lockfile[256], display_name[8]; + + wxs = zalloc(sizeof *wxs); + wxs->process.cleanup = weston_xserver_cleanup; + wxs->wl_display = display; + wxs->compositor = compositor; + + wxs->display = 0; + + retry: + if (create_lockfile(wxs->display, lockfile, sizeof lockfile) < 0) { + if (errno == EAGAIN) { + goto retry; + } else if (errno == EEXIST) { + wxs->display++; + goto retry; + } else { + free(wxs); + return -1; + } + } + + wxs->abstract_fd = bind_to_abstract_socket(wxs->display); + if (wxs->abstract_fd < 0 && errno == EADDRINUSE) { + wxs->display++; + unlink(lockfile); + goto retry; + } + + wxs->unix_fd = bind_to_unix_socket(wxs->display); + if (wxs->unix_fd < 0) { + unlink(lockfile); + close(wxs->abstract_fd); + free(wxs); + return -1; + } + + snprintf(display_name, sizeof display_name, ":%d", wxs->display); + weston_log("xserver listening on display %s\n", display_name); + setenv("DISPLAY", display_name, 1); + + wxs->loop = wl_display_get_event_loop(display); + wxs->abstract_source = + wl_event_loop_add_fd(wxs->loop, wxs->abstract_fd, + WL_EVENT_READABLE, + weston_xserver_handle_event, wxs); + wxs->unix_source = + wl_event_loop_add_fd(wxs->loop, wxs->unix_fd, + WL_EVENT_READABLE, + weston_xserver_handle_event, wxs); + + wl_global_create(display, &xserver_interface, 1, wxs, bind_xserver); + + wxs->destroy_listener.notify = weston_xserver_destroy; + wl_signal_add(&compositor->destroy_signal, &wxs->destroy_listener); + + return 0; +} diff --git a/xwayland/selection.c b/xwayland/selection.c new file mode 100644 index 00000000..b694477e --- /dev/null +++ b/xwayland/selection.c @@ -0,0 +1,712 @@ +/* + * Copyright © 2012 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "xwayland.h" + +static int +writable_callback(int fd, uint32_t mask, void *data) +{ + struct weston_wm *wm = data; + unsigned char *property; + int len, remainder; + + property = xcb_get_property_value(wm->property_reply); + remainder = xcb_get_property_value_length(wm->property_reply) - + wm->property_start; + + len = write(fd, property + wm->property_start, remainder); + if (len == -1) { + free(wm->property_reply); + wm->property_reply = NULL; + if (wm->property_source) + wl_event_source_remove(wm->property_source); + close(fd); + weston_log("write error to target fd: %m\n"); + return 1; + } + + weston_log("wrote %d (chunk size %d) of %d bytes\n", + wm->property_start + len, + len, xcb_get_property_value_length(wm->property_reply)); + + wm->property_start += len; + if (len == remainder) { + free(wm->property_reply); + wm->property_reply = NULL; + if (wm->property_source) + wl_event_source_remove(wm->property_source); + + if (wm->incr) { + xcb_delete_property(wm->conn, + wm->selection_window, + wm->atom.wl_selection); + } else { + weston_log("transfer complete\n"); + close(fd); + } + } + + return 1; +} + +static void +weston_wm_write_property(struct weston_wm *wm, xcb_get_property_reply_t *reply) +{ + wm->property_start = 0; + wm->property_reply = reply; + writable_callback(wm->data_source_fd, WL_EVENT_WRITABLE, wm); + + if (wm->property_reply) + wm->property_source = + wl_event_loop_add_fd(wm->server->loop, + wm->data_source_fd, + WL_EVENT_WRITABLE, + writable_callback, wm); +} + +static void +weston_wm_get_incr_chunk(struct weston_wm *wm) +{ + xcb_get_property_cookie_t cookie; + xcb_get_property_reply_t *reply; + + cookie = xcb_get_property(wm->conn, + 0, /* delete */ + wm->selection_window, + wm->atom.wl_selection, + XCB_GET_PROPERTY_TYPE_ANY, + 0, /* offset */ + 0x1fffffff /* length */); + + reply = xcb_get_property_reply(wm->conn, cookie, NULL); + + dump_property(wm, wm->atom.wl_selection, reply); + + if (xcb_get_property_value_length(reply) > 0) { + weston_wm_write_property(wm, reply); + } else { + weston_log("transfer complete\n"); + close(wm->data_source_fd); + free(reply); + } +} + +struct x11_data_source { + struct weston_data_source base; + struct weston_wm *wm; +}; + +static void +data_source_accept(struct weston_data_source *source, + uint32_t time, const char *mime_type) +{ +} + +static void +data_source_send(struct weston_data_source *base, + const char *mime_type, int32_t fd) +{ + struct x11_data_source *source = (struct x11_data_source *) base; + struct weston_wm *wm = source->wm; + + if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) { + /* Get data for the utf8_string target */ + xcb_convert_selection(wm->conn, + wm->selection_window, + wm->atom.clipboard, + wm->atom.utf8_string, + wm->atom.wl_selection, + XCB_TIME_CURRENT_TIME); + + xcb_flush(wm->conn); + + fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK); + wm->data_source_fd = fd; + } +} + +static void +data_source_cancel(struct weston_data_source *source) +{ +} + +static void +weston_wm_get_selection_targets(struct weston_wm *wm) +{ + struct x11_data_source *source; + struct weston_compositor *compositor; + struct weston_seat *seat = weston_wm_pick_seat(wm); + xcb_get_property_cookie_t cookie; + xcb_get_property_reply_t *reply; + xcb_atom_t *value; + char **p; + uint32_t i; + + cookie = xcb_get_property(wm->conn, + 1, /* delete */ + wm->selection_window, + wm->atom.wl_selection, + XCB_GET_PROPERTY_TYPE_ANY, + 0, /* offset */ + 4096 /* length */); + + reply = xcb_get_property_reply(wm->conn, cookie, NULL); + + dump_property(wm, wm->atom.wl_selection, reply); + + if (reply->type != XCB_ATOM_ATOM) { + free(reply); + return; + } + + source = malloc(sizeof *source); + if (source == NULL) + return; + + wl_signal_init(&source->base.destroy_signal); + source->base.accept = data_source_accept; + source->base.send = data_source_send; + source->base.cancel = data_source_cancel; + source->wm = wm; + + wl_array_init(&source->base.mime_types); + value = xcb_get_property_value(reply); + for (i = 0; i < reply->value_len; i++) { + if (value[i] == wm->atom.utf8_string) { + p = wl_array_add(&source->base.mime_types, sizeof *p); + if (p) + *p = strdup("text/plain;charset=utf-8"); + } + } + + compositor = wm->server->compositor; + weston_seat_set_selection(seat, &source->base, + wl_display_next_serial(compositor->wl_display)); + + free(reply); +} + +static void +weston_wm_get_selection_data(struct weston_wm *wm) +{ + xcb_get_property_cookie_t cookie; + xcb_get_property_reply_t *reply; + + cookie = xcb_get_property(wm->conn, + 1, /* delete */ + wm->selection_window, + wm->atom.wl_selection, + XCB_GET_PROPERTY_TYPE_ANY, + 0, /* offset */ + 0x1fffffff /* length */); + + reply = xcb_get_property_reply(wm->conn, cookie, NULL); + + if (reply->type == wm->atom.incr) { + dump_property(wm, wm->atom.wl_selection, reply); + wm->incr = 1; + free(reply); + } else { + dump_property(wm, wm->atom.wl_selection, reply); + wm->incr = 0; + weston_wm_write_property(wm, reply); + } +} + +static void +weston_wm_handle_selection_notify(struct weston_wm *wm, + xcb_generic_event_t *event) +{ + xcb_selection_notify_event_t *selection_notify = + (xcb_selection_notify_event_t *) event; + + if (selection_notify->property == XCB_ATOM_NONE) { + /* convert selection failed */ + } else if (selection_notify->target == wm->atom.targets) { + weston_wm_get_selection_targets(wm); + } else { + weston_wm_get_selection_data(wm); + } +} + +static const size_t incr_chunk_size = 64 * 1024; + +static void +weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property) +{ + xcb_selection_notify_event_t selection_notify; + + memset(&selection_notify, 0, sizeof selection_notify); + selection_notify.response_type = XCB_SELECTION_NOTIFY; + selection_notify.sequence = 0; + selection_notify.time = wm->selection_request.time; + selection_notify.requestor = wm->selection_request.requestor; + selection_notify.selection = wm->selection_request.selection; + selection_notify.target = wm->selection_request.target; + selection_notify.property = property; + + xcb_send_event(wm->conn, 0, /* propagate */ + wm->selection_request.requestor, + XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify); +} + +static void +weston_wm_send_targets(struct weston_wm *wm) +{ + xcb_atom_t targets[] = { + wm->atom.timestamp, + wm->atom.targets, + wm->atom.utf8_string, + /* wm->atom.compound_text, */ + wm->atom.text, + /* wm->atom.string */ + }; + + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + wm->selection_request.requestor, + wm->selection_request.property, + XCB_ATOM_ATOM, + 32, /* format */ + ARRAY_LENGTH(targets), targets); + + weston_wm_send_selection_notify(wm, wm->selection_request.property); +} + +static void +weston_wm_send_timestamp(struct weston_wm *wm) +{ + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + wm->selection_request.requestor, + wm->selection_request.property, + XCB_ATOM_INTEGER, + 32, /* format */ + 1, &wm->selection_timestamp); + + weston_wm_send_selection_notify(wm, wm->selection_request.property); +} + +static int +weston_wm_flush_source_data(struct weston_wm *wm) +{ + int length; + + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + wm->selection_request.requestor, + wm->selection_request.property, + wm->selection_target, + 8, /* format */ + wm->source_data.size, + wm->source_data.data); + wm->selection_property_set = 1; + length = wm->source_data.size; + wm->source_data.size = 0; + + return length; +} + +static int +weston_wm_read_data_source(int fd, uint32_t mask, void *data) +{ + struct weston_wm *wm = data; + int len, current, available; + void *p; + + current = wm->source_data.size; + if (wm->source_data.size < incr_chunk_size) + p = wl_array_add(&wm->source_data, incr_chunk_size); + else + p = (char *) wm->source_data.data + wm->source_data.size; + available = wm->source_data.alloc - current; + + len = read(fd, p, available); + if (len == -1) { + weston_log("read error from data source: %m\n"); + weston_wm_send_selection_notify(wm, XCB_ATOM_NONE); + wl_event_source_remove(wm->property_source); + close(fd); + wl_array_release(&wm->source_data); + } + + weston_log("read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n", + len, available, mask, len, (char *) p); + + wm->source_data.size = current + len; + if (wm->source_data.size >= incr_chunk_size) { + if (!wm->incr) { + weston_log("got %zu bytes, starting incr\n", + wm->source_data.size); + wm->incr = 1; + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + wm->selection_request.requestor, + wm->selection_request.property, + wm->atom.incr, + 32, /* format */ + 1, &incr_chunk_size); + wm->selection_property_set = 1; + wm->flush_property_on_delete = 1; + wl_event_source_remove(wm->property_source); + weston_wm_send_selection_notify(wm, wm->selection_request.property); + } else if (wm->selection_property_set) { + weston_log("got %zu bytes, waiting for " + "property delete\n", wm->source_data.size); + + wm->flush_property_on_delete = 1; + wl_event_source_remove(wm->property_source); + } else { + weston_log("got %zu bytes, " + "property deleted, seting new property\n", + wm->source_data.size); + weston_wm_flush_source_data(wm); + } + } else if (len == 0 && !wm->incr) { + weston_log("non-incr transfer complete\n"); + /* Non-incr transfer all done. */ + weston_wm_flush_source_data(wm); + weston_wm_send_selection_notify(wm, wm->selection_request.property); + xcb_flush(wm->conn); + wl_event_source_remove(wm->property_source); + close(fd); + wl_array_release(&wm->source_data); + wm->selection_request.requestor = XCB_NONE; + } else if (len == 0 && wm->incr) { + weston_log("incr transfer complete\n"); + + wm->flush_property_on_delete = 1; + if (wm->selection_property_set) { + weston_log("got %zu bytes, waiting for " + "property delete\n", wm->source_data.size); + } else { + weston_log("got %zu bytes, " + "property deleted, seting new property\n", + wm->source_data.size); + weston_wm_flush_source_data(wm); + } + xcb_flush(wm->conn); + wl_event_source_remove(wm->property_source); + close(wm->data_source_fd); + wm->data_source_fd = -1; + close(fd); + } else { + weston_log("nothing happened, buffered the bytes\n"); + } + + return 1; +} + +static void +weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type) +{ + struct weston_data_source *source; + struct weston_seat *seat = weston_wm_pick_seat(wm); + int p[2]; + + if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) { + weston_log("pipe2 failed: %m\n"); + weston_wm_send_selection_notify(wm, XCB_ATOM_NONE); + return; + } + + wl_array_init(&wm->source_data); + wm->selection_target = target; + wm->data_source_fd = p[0]; + wm->property_source = wl_event_loop_add_fd(wm->server->loop, + wm->data_source_fd, + WL_EVENT_READABLE, + weston_wm_read_data_source, + wm); + + source = seat->selection_data_source; + source->send(source, mime_type, p[1]); + close(p[1]); +} + +static void +weston_wm_send_incr_chunk(struct weston_wm *wm) +{ + int length; + + weston_log("property deleted\n"); + + wm->selection_property_set = 0; + if (wm->flush_property_on_delete) { + weston_log("setting new property, %zu bytes\n", + wm->source_data.size); + wm->flush_property_on_delete = 0; + length = weston_wm_flush_source_data(wm); + + if (wm->data_source_fd >= 0) { + wm->property_source = + wl_event_loop_add_fd(wm->server->loop, + wm->data_source_fd, + WL_EVENT_READABLE, + weston_wm_read_data_source, + wm); + } else if (length > 0) { + /* Transfer is all done, but queue a flush for + * the delete of the last chunk so we can set + * the 0 sized propert to signal the end of + * the transfer. */ + wm->flush_property_on_delete = 1; + wl_array_release(&wm->source_data); + } else { + wm->selection_request.requestor = XCB_NONE; + } + } +} + +static int +weston_wm_handle_selection_property_notify(struct weston_wm *wm, + xcb_generic_event_t *event) +{ + xcb_property_notify_event_t *property_notify = + (xcb_property_notify_event_t *) event; + + if (property_notify->window == wm->selection_window) { + if (property_notify->state == XCB_PROPERTY_NEW_VALUE && + property_notify->atom == wm->atom.wl_selection && + wm->incr) + weston_wm_get_incr_chunk(wm); + return 1; + } else if (property_notify->window == wm->selection_request.requestor) { + if (property_notify->state == XCB_PROPERTY_DELETE && + property_notify->atom == wm->selection_request.property && + wm->incr) + weston_wm_send_incr_chunk(wm); + return 1; + } + + return 0; +} + +static void +weston_wm_handle_selection_request(struct weston_wm *wm, + xcb_generic_event_t *event) +{ + xcb_selection_request_event_t *selection_request = + (xcb_selection_request_event_t *) event; + + weston_log("selection request, %s, ", + get_atom_name(wm->conn, selection_request->selection)); + weston_log_continue("target %s, ", + get_atom_name(wm->conn, selection_request->target)); + weston_log_continue("property %s\n", + get_atom_name(wm->conn, selection_request->property)); + + wm->selection_request = *selection_request; + wm->incr = 0; + wm->flush_property_on_delete = 0; + + if (selection_request->selection == wm->atom.clipboard_manager) { + /* The weston clipboard should already have grabbed + * the first target, so just send selection notify + * now. This isn't synchronized with the clipboard + * finishing getting the data, so there's a race here. */ + weston_wm_send_selection_notify(wm, wm->selection_request.property); + return; + } + + if (selection_request->target == wm->atom.targets) { + weston_wm_send_targets(wm); + } else if (selection_request->target == wm->atom.timestamp) { + weston_wm_send_timestamp(wm); + } else if (selection_request->target == wm->atom.utf8_string || + selection_request->target == wm->atom.text) { + weston_wm_send_data(wm, wm->atom.utf8_string, + "text/plain;charset=utf-8"); + } else { + weston_log("can only handle UTF8_STRING targets...\n"); + weston_wm_send_selection_notify(wm, XCB_ATOM_NONE); + } +} + +static int +weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm, + xcb_generic_event_t *event) +{ + xcb_xfixes_selection_notify_event_t *xfixes_selection_notify = + (xcb_xfixes_selection_notify_event_t *) event; + struct weston_compositor *compositor; + struct weston_seat *seat = weston_wm_pick_seat(wm); + uint32_t serial; + + if (xfixes_selection_notify->selection != wm->atom.clipboard) + return 0; + + weston_log("xfixes selection notify event: owner %d\n", + xfixes_selection_notify->owner); + + if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) { + if (wm->selection_owner != wm->selection_window) { + /* A real X client selection went away, not our + * proxy selection. Clear the wayland selection. */ + compositor = wm->server->compositor; + serial = wl_display_next_serial(compositor->wl_display); + weston_seat_set_selection(seat, NULL, serial); + } + + wm->selection_owner = XCB_WINDOW_NONE; + + return 1; + } + + wm->selection_owner = xfixes_selection_notify->owner; + + /* We have to use XCB_TIME_CURRENT_TIME when we claim the + * selection, so grab the actual timestamp here so we can + * answer TIMESTAMP conversion requests correctly. */ + if (xfixes_selection_notify->owner == wm->selection_window) { + wm->selection_timestamp = xfixes_selection_notify->timestamp; + weston_log("our window, skipping\n"); + return 1; + } + + wm->incr = 0; + xcb_convert_selection(wm->conn, wm->selection_window, + wm->atom.clipboard, + wm->atom.targets, + wm->atom.wl_selection, + xfixes_selection_notify->timestamp); + + xcb_flush(wm->conn); + + return 1; +} + +int +weston_wm_handle_selection_event(struct weston_wm *wm, + xcb_generic_event_t *event) +{ + switch (event->response_type & ~0x80) { + case XCB_SELECTION_NOTIFY: + weston_wm_handle_selection_notify(wm, event); + return 1; + case XCB_PROPERTY_NOTIFY: + return weston_wm_handle_selection_property_notify(wm, event); + case XCB_SELECTION_REQUEST: + weston_wm_handle_selection_request(wm, event); + return 1; + } + + switch (event->response_type - wm->xfixes->first_event) { + case XCB_XFIXES_SELECTION_NOTIFY: + return weston_wm_handle_xfixes_selection_notify(wm, event); + } + + return 0; +} + +static void +weston_wm_set_selection(struct wl_listener *listener, void *data) +{ + struct weston_seat *seat = data; + struct weston_wm *wm = + container_of(listener, struct weston_wm, selection_listener); + struct weston_data_source *source = seat->selection_data_source; + const char **p, **end; + int has_text_plain = 0; + + if (source == NULL) { + if (wm->selection_owner == wm->selection_window) + xcb_set_selection_owner(wm->conn, + XCB_ATOM_NONE, + wm->atom.clipboard, + wm->selection_timestamp); + return; + } + + if (source->send == data_source_send) + return; + + p = source->mime_types.data; + end = (const char **) + ((char *) source->mime_types.data + source->mime_types.size); + while (p < end) { + weston_log(" %s\n", *p); + if (strcmp(*p, "text/plain") == 0 || + strcmp(*p, "text/plain;charset=utf-8") == 0) + has_text_plain = 1; + p++; + } + + if (has_text_plain) { + xcb_set_selection_owner(wm->conn, + wm->selection_window, + wm->atom.clipboard, + XCB_TIME_CURRENT_TIME); + } else { + xcb_set_selection_owner(wm->conn, + XCB_ATOM_NONE, + wm->atom.clipboard, + XCB_TIME_CURRENT_TIME); + } +} + +void +weston_wm_selection_init(struct weston_wm *wm) +{ + struct weston_seat *seat; + uint32_t values[1], mask; + + wm->selection_request.requestor = XCB_NONE; + + values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE; + wm->selection_window = xcb_generate_id(wm->conn); + xcb_create_window(wm->conn, + XCB_COPY_FROM_PARENT, + wm->selection_window, + wm->screen->root, + 0, 0, + 10, 10, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + wm->screen->root_visual, + XCB_CW_EVENT_MASK, values); + + xcb_set_selection_owner(wm->conn, + wm->selection_window, + wm->atom.clipboard_manager, + XCB_TIME_CURRENT_TIME); + + mask = + XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | + XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | + XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; + xcb_xfixes_select_selection_input(wm->conn, wm->selection_window, + wm->atom.clipboard, mask); + + seat = weston_wm_pick_seat(wm); + wm->selection_listener.notify = weston_wm_set_selection; + wl_signal_add(&seat->selection_signal, &wm->selection_listener); + + weston_wm_set_selection(&wm->selection_listener, seat); +} diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c new file mode 100644 index 00000000..70c8cf7c --- /dev/null +++ b/xwayland/window-manager.c @@ -0,0 +1,2268 @@ +/* + * Copyright © 2011 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xwayland.h" + +#include "cairo-util.h" +#include "compositor.h" +#include "xserver-server-protocol.h" +#include "hash.h" + +struct wm_size_hints { + uint32_t flags; + int32_t x, y; + int32_t width, height; /* should set so old wm's don't mess up */ + int32_t min_width, min_height; + int32_t max_width, max_height; + int32_t width_inc, height_inc; + struct { + int32_t x; + int32_t y; + } min_aspect, max_aspect; + int32_t base_width, base_height; + int32_t win_gravity; +}; + +#define USPosition (1L << 0) +#define USSize (1L << 1) +#define PPosition (1L << 2) +#define PSize (1L << 3) +#define PMinSize (1L << 4) +#define PMaxSize (1L << 5) +#define PResizeInc (1L << 6) +#define PAspect (1L << 7) +#define PBaseSize (1L << 8) +#define PWinGravity (1L << 9) + +struct motif_wm_hints { + uint32_t flags; + uint32_t functions; + uint32_t decorations; + int32_t input_mode; + uint32_t status; +}; + +#define MWM_HINTS_FUNCTIONS (1L << 0) +#define MWM_HINTS_DECORATIONS (1L << 1) +#define MWM_HINTS_INPUT_MODE (1L << 2) +#define MWM_HINTS_STATUS (1L << 3) + +#define MWM_FUNC_ALL (1L << 0) +#define MWM_FUNC_RESIZE (1L << 1) +#define MWM_FUNC_MOVE (1L << 2) +#define MWM_FUNC_MINIMIZE (1L << 3) +#define MWM_FUNC_MAXIMIZE (1L << 4) +#define MWM_FUNC_CLOSE (1L << 5) + +#define MWM_DECOR_ALL (1L << 0) +#define MWM_DECOR_BORDER (1L << 1) +#define MWM_DECOR_RESIZEH (1L << 2) +#define MWM_DECOR_TITLE (1L << 3) +#define MWM_DECOR_MENU (1L << 4) +#define MWM_DECOR_MINIMIZE (1L << 5) +#define MWM_DECOR_MAXIMIZE (1L << 6) + +#define MWM_INPUT_MODELESS 0 +#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1 +#define MWM_INPUT_SYSTEM_MODAL 2 +#define MWM_INPUT_FULL_APPLICATION_MODAL 3 +#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL + +#define MWM_TEAROFF_WINDOW (1L<<0) + +#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 +#define _NET_WM_MOVERESIZE_SIZE_TOP 1 +#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 +#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 +#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 +#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */ +#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */ +#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ +#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ + +struct weston_wm_window { + struct weston_wm *wm; + xcb_window_t id; + xcb_window_t frame_id; + struct frame *frame; + cairo_surface_t *cairo_surface; + struct weston_surface *surface; + struct shell_surface *shsurf; + struct weston_view *view; + struct wl_listener surface_destroy_listener; + struct wl_event_source *repaint_source; + struct wl_event_source *configure_source; + int properties_dirty; + int pid; + char *machine; + char *class; + char *name; + struct weston_wm_window *transient_for; + uint32_t protocols; + xcb_atom_t type; + int width, height; + int x, y; + int saved_width, saved_height; + int decorate; + int override_redirect; + int fullscreen; + int has_alpha; + int delete_window; + struct wm_size_hints size_hints; + struct motif_wm_hints motif_hints; +}; + +static struct weston_wm_window * +get_wm_window(struct weston_surface *surface); + +static void +weston_wm_window_schedule_repaint(struct weston_wm_window *window); + +static int __attribute__ ((format (printf, 1, 2))) +wm_log(const char *fmt, ...) +{ +#ifdef WM_DEBUG + int l; + va_list argp; + + va_start(argp, fmt); + l = weston_vlog(fmt, argp); + va_end(argp); + + return l; +#else + return 0; +#endif +} + +static int __attribute__ ((format (printf, 1, 2))) +wm_log_continue(const char *fmt, ...) +{ +#ifdef WM_DEBUG + int l; + va_list argp; + + va_start(argp, fmt); + l = weston_vlog_continue(fmt, argp); + va_end(argp); + + return l; +#else + return 0; +#endif +} + + +const char * +get_atom_name(xcb_connection_t *c, xcb_atom_t atom) +{ + xcb_get_atom_name_cookie_t cookie; + xcb_get_atom_name_reply_t *reply; + xcb_generic_error_t *e; + static char buffer[64]; + + if (atom == XCB_ATOM_NONE) + return "None"; + + cookie = xcb_get_atom_name (c, atom); + reply = xcb_get_atom_name_reply (c, cookie, &e); + + if(reply) { + snprintf(buffer, sizeof buffer, "%.*s", + xcb_get_atom_name_name_length (reply), + xcb_get_atom_name_name (reply)); + } else { + snprintf(buffer, sizeof buffer, "(atom %u)", atom); + } + + free(reply); + + return buffer; +} + +static xcb_cursor_t +xcb_cursor_image_load_cursor(struct weston_wm *wm, const XcursorImage *img) +{ + xcb_connection_t *c = wm->conn; + xcb_screen_iterator_t s = xcb_setup_roots_iterator(xcb_get_setup(c)); + xcb_screen_t *screen = s.data; + xcb_gcontext_t gc; + xcb_pixmap_t pix; + xcb_render_picture_t pic; + xcb_cursor_t cursor; + int stride = img->width * 4; + + pix = xcb_generate_id(c); + xcb_create_pixmap(c, 32, pix, screen->root, img->width, img->height); + + pic = xcb_generate_id(c); + xcb_render_create_picture(c, pic, pix, wm->format_rgba.id, 0, 0); + + gc = xcb_generate_id(c); + xcb_create_gc(c, gc, pix, 0, 0); + + xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, + img->width, img->height, 0, 0, 0, 32, + stride * img->height, (uint8_t *) img->pixels); + xcb_free_gc(c, gc); + + cursor = xcb_generate_id(c); + xcb_render_create_cursor(c, cursor, pic, img->xhot, img->yhot); + + xcb_render_free_picture(c, pic); + xcb_free_pixmap(c, pix); + + return cursor; +} + +static xcb_cursor_t +xcb_cursor_images_load_cursor(struct weston_wm *wm, const XcursorImages *images) +{ + /* TODO: treat animated cursors as well */ + if (images->nimage != 1) + return -1; + + return xcb_cursor_image_load_cursor(wm, images->images[0]); +} + +static xcb_cursor_t +xcb_cursor_library_load_cursor(struct weston_wm *wm, const char *file) +{ + xcb_cursor_t cursor; + XcursorImages *images; + char *v = NULL; + int size = 0; + + if (!file) + return 0; + + v = getenv ("XCURSOR_SIZE"); + if (v) + size = atoi(v); + + if (!size) + size = 32; + + images = XcursorLibraryLoadImages (file, NULL, size); + if (!images) + return -1; + + cursor = xcb_cursor_images_load_cursor (wm, images); + XcursorImagesDestroy (images); + + return cursor; +} + +void +dump_property(struct weston_wm *wm, + xcb_atom_t property, xcb_get_property_reply_t *reply) +{ + int32_t *incr_value; + const char *text_value, *name; + xcb_atom_t *atom_value; + int width, len; + uint32_t i; + + width = wm_log_continue("%s: ", get_atom_name(wm->conn, property)); + if (reply == NULL) { + wm_log_continue("(no reply)\n"); + return; + } + + width += wm_log_continue("%s/%d, length %d (value_len %d): ", + get_atom_name(wm->conn, reply->type), + reply->format, + xcb_get_property_value_length(reply), + reply->value_len); + + if (reply->type == wm->atom.incr) { + incr_value = xcb_get_property_value(reply); + wm_log_continue("%d\n", *incr_value); + } else if (reply->type == wm->atom.utf8_string || + reply->type == wm->atom.string) { + text_value = xcb_get_property_value(reply); + if (reply->value_len > 40) + len = 40; + else + len = reply->value_len; + wm_log_continue("\"%.*s\"\n", len, text_value); + } else if (reply->type == XCB_ATOM_ATOM) { + atom_value = xcb_get_property_value(reply); + for (i = 0; i < reply->value_len; i++) { + name = get_atom_name(wm->conn, atom_value[i]); + if (width + strlen(name) + 2 > 78) { + wm_log_continue("\n "); + width = 4; + } else if (i > 0) { + width += wm_log_continue(", "); + } + + width += wm_log_continue("%s", name); + } + wm_log_continue("\n"); + } else { + wm_log_continue("huh?\n"); + } +} + +static void +read_and_dump_property(struct weston_wm *wm, + xcb_window_t window, xcb_atom_t property) +{ + xcb_get_property_reply_t *reply; + xcb_get_property_cookie_t cookie; + + cookie = xcb_get_property(wm->conn, 0, window, + property, XCB_ATOM_ANY, 0, 2048); + reply = xcb_get_property_reply(wm->conn, cookie, NULL); + + dump_property(wm, property, reply); + + free(reply); +} + +/* We reuse some predefined, but otherwise useles atoms */ +#define TYPE_WM_PROTOCOLS XCB_ATOM_CUT_BUFFER0 +#define TYPE_MOTIF_WM_HINTS XCB_ATOM_CUT_BUFFER1 +#define TYPE_NET_WM_STATE XCB_ATOM_CUT_BUFFER2 +#define TYPE_WM_NORMAL_HINTS XCB_ATOM_CUT_BUFFER3 + +static void +weston_wm_window_read_properties(struct weston_wm_window *window) +{ + struct weston_wm *wm = window->wm; + struct weston_shell_interface *shell_interface = + &wm->server->compositor->shell_interface; + +#define F(field) offsetof(struct weston_wm_window, field) + const struct { + xcb_atom_t atom; + xcb_atom_t type; + int offset; + } props[] = { + { XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, F(class) }, + { XCB_ATOM_WM_NAME, XCB_ATOM_STRING, F(name) }, + { XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, F(transient_for) }, + { wm->atom.wm_protocols, TYPE_WM_PROTOCOLS, F(protocols) }, + { wm->atom.wm_normal_hints, TYPE_WM_NORMAL_HINTS, F(protocols) }, + { wm->atom.net_wm_state, TYPE_NET_WM_STATE }, + { wm->atom.net_wm_window_type, XCB_ATOM_ATOM, F(type) }, + { wm->atom.net_wm_name, XCB_ATOM_STRING, F(name) }, + { wm->atom.net_wm_pid, XCB_ATOM_CARDINAL, F(pid) }, + { wm->atom.motif_wm_hints, TYPE_MOTIF_WM_HINTS, 0 }, + { wm->atom.wm_client_machine, XCB_ATOM_WM_CLIENT_MACHINE, F(machine) }, + }; +#undef F + + xcb_get_property_cookie_t cookie[ARRAY_LENGTH(props)]; + xcb_get_property_reply_t *reply; + void *p; + uint32_t *xid; + xcb_atom_t *atom; + uint32_t i; + + if (!window->properties_dirty) + return; + window->properties_dirty = 0; + + for (i = 0; i < ARRAY_LENGTH(props); i++) + cookie[i] = xcb_get_property(wm->conn, + 0, /* delete */ + window->id, + props[i].atom, + XCB_ATOM_ANY, 0, 2048); + + window->decorate = !window->override_redirect; + window->size_hints.flags = 0; + window->motif_hints.flags = 0; + window->delete_window = 0; + + for (i = 0; i < ARRAY_LENGTH(props); i++) { + reply = xcb_get_property_reply(wm->conn, cookie[i], NULL); + if (!reply) + /* Bad window, typically */ + continue; + if (reply->type == XCB_ATOM_NONE) { + /* No such property */ + free(reply); + continue; + } + + p = ((char *) window + props[i].offset); + + switch (props[i].type) { + case XCB_ATOM_WM_CLIENT_MACHINE: + case XCB_ATOM_STRING: + /* FIXME: We're using this for both string and + utf8_string */ + if (*(char **) p) + free(*(char **) p); + + *(char **) p = + strndup(xcb_get_property_value(reply), + xcb_get_property_value_length(reply)); + break; + case XCB_ATOM_WINDOW: + xid = xcb_get_property_value(reply); + *(struct weston_wm_window **) p = + hash_table_lookup(wm->window_hash, *xid); + break; + case XCB_ATOM_CARDINAL: + case XCB_ATOM_ATOM: + atom = xcb_get_property_value(reply); + *(xcb_atom_t *) p = *atom; + break; + case TYPE_WM_PROTOCOLS: + atom = xcb_get_property_value(reply); + for (i = 0; i < reply->value_len; i++) + if (atom[i] == wm->atom.wm_delete_window) + window->delete_window = 1; + break; + + break; + case TYPE_WM_NORMAL_HINTS: + memcpy(&window->size_hints, + xcb_get_property_value(reply), + sizeof window->size_hints); + break; + case TYPE_NET_WM_STATE: + window->fullscreen = 0; + atom = xcb_get_property_value(reply); + for (i = 0; i < reply->value_len; i++) + if (atom[i] == wm->atom.net_wm_state_fullscreen) + window->fullscreen = 1; + break; + case TYPE_MOTIF_WM_HINTS: + memcpy(&window->motif_hints, + xcb_get_property_value(reply), + sizeof window->motif_hints); + if (window->motif_hints.flags & MWM_HINTS_DECORATIONS) + window->decorate = + window->motif_hints.decorations > 0; + break; + default: + break; + } + free(reply); + } + + if (window->shsurf && window->name) + shell_interface->set_title(window->shsurf, window->name); + if (window->frame && window->name) + frame_set_title(window->frame, window->name); +} + +static void +weston_wm_window_get_frame_size(struct weston_wm_window *window, + int *width, int *height) +{ + struct theme *t = window->wm->theme; + + if (window->fullscreen) { + *width = window->width; + *height = window->height; + } else if (window->decorate && window->frame) { + *width = frame_width(window->frame); + *height = frame_height(window->frame); + } else { + *width = window->width + t->margin * 2; + *height = window->height + t->margin * 2; + } +} + +static void +weston_wm_window_get_child_position(struct weston_wm_window *window, + int *x, int *y) +{ + struct theme *t = window->wm->theme; + + if (window->fullscreen) { + *x = 0; + *y = 0; + } else if (window->decorate && window->frame) { + frame_interior(window->frame, x, y, NULL, NULL); + } else { + *x = t->margin; + *y = t->margin; + } +} + +static void +weston_wm_window_send_configure_notify(struct weston_wm_window *window) +{ + xcb_configure_notify_event_t configure_notify; + struct weston_wm *wm = window->wm; + int x, y; + + weston_wm_window_get_child_position(window, &x, &y); + configure_notify.response_type = XCB_CONFIGURE_NOTIFY; + configure_notify.pad0 = 0; + configure_notify.event = window->id; + configure_notify.window = window->id; + configure_notify.above_sibling = XCB_WINDOW_NONE; + configure_notify.x = x; + configure_notify.y = y; + configure_notify.width = window->width; + configure_notify.height = window->height; + configure_notify.border_width = 0; + configure_notify.override_redirect = 0; + configure_notify.pad1 = 0; + + xcb_send_event(wm->conn, 0, window->id, + XCB_EVENT_MASK_STRUCTURE_NOTIFY, + (char *) &configure_notify); +} + +static void +weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_configure_request_event_t *configure_request = + (xcb_configure_request_event_t *) event; + struct weston_wm_window *window; + uint32_t mask, values[16]; + int x, y, width, height, i = 0; + + wm_log("XCB_CONFIGURE_REQUEST (window %d) %d,%d @ %dx%d\n", + configure_request->window, + configure_request->x, configure_request->y, + configure_request->width, configure_request->height); + + window = hash_table_lookup(wm->window_hash, configure_request->window); + + if (window->fullscreen) { + weston_wm_window_send_configure_notify(window); + return; + } + + if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH) + window->width = configure_request->width; + if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT) + window->height = configure_request->height; + + if (window->frame) + frame_resize_inside(window->frame, window->width, window->height); + + weston_wm_window_get_child_position(window, &x, &y); + values[i++] = x; + values[i++] = y; + values[i++] = window->width; + values[i++] = window->height; + values[i++] = 0; + mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | + XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | + XCB_CONFIG_WINDOW_BORDER_WIDTH; + if (configure_request->value_mask & XCB_CONFIG_WINDOW_SIBLING) { + values[i++] = configure_request->sibling; + mask |= XCB_CONFIG_WINDOW_SIBLING; + } + if (configure_request->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) { + values[i++] = configure_request->stack_mode; + mask |= XCB_CONFIG_WINDOW_STACK_MODE; + } + + xcb_configure_window(wm->conn, window->id, mask, values); + + weston_wm_window_get_frame_size(window, &width, &height); + values[0] = width; + values[1] = height; + mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; + xcb_configure_window(wm->conn, window->frame_id, mask, values); + + weston_wm_window_schedule_repaint(window); +} + +static int +our_resource(struct weston_wm *wm, uint32_t id) +{ + const xcb_setup_t *setup; + + setup = xcb_get_setup(wm->conn); + + return (id & ~setup->resource_id_mask) == setup->resource_id_base; +} + +static void +weston_wm_handle_configure_notify(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_configure_notify_event_t *configure_notify = + (xcb_configure_notify_event_t *) event; + struct weston_wm_window *window; + + wm_log("XCB_CONFIGURE_NOTIFY (window %d) %d,%d @ %dx%d\n", + configure_notify->window, + configure_notify->x, configure_notify->y, + configure_notify->width, configure_notify->height); + + window = hash_table_lookup(wm->window_hash, configure_notify->window); + window->x = configure_notify->x; + window->y = configure_notify->y; + if (window->override_redirect) { + window->width = configure_notify->width; + window->height = configure_notify->height; + if (window->frame) + frame_resize_inside(window->frame, + window->width, window->height); + } +} + +static void +weston_wm_kill_client(struct wl_listener *listener, void *data) +{ + struct weston_surface *surface = data; + struct weston_wm_window *window = get_wm_window(surface); + char name[1024]; + + if (!window) + return; + + gethostname(name, 1024); + + /* this is only one heuristic to guess the PID of a client is valid, + * assuming it's compliant with icccm and ewmh. Non-compliants and + * remote applications of course fail. */ + if (!strcmp(window->machine, name) && window->pid != 0) + kill(window->pid, SIGKILL); +} + +static void +weston_wm_window_activate(struct wl_listener *listener, void *data) +{ + struct weston_surface *surface = data; + struct weston_wm_window *window = NULL; + struct weston_wm *wm = + container_of(listener, struct weston_wm, activate_listener); + xcb_client_message_event_t client_message; + + if (surface) { + window = get_wm_window(surface); + } + + if (window) { + client_message.response_type = XCB_CLIENT_MESSAGE; + client_message.format = 32; + client_message.window = window->id; + client_message.type = wm->atom.wm_protocols; + client_message.data.data32[0] = wm->atom.wm_take_focus; + client_message.data.data32[1] = XCB_TIME_CURRENT_TIME; + + xcb_send_event(wm->conn, 0, window->id, + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + (char *) &client_message); + + xcb_set_input_focus (wm->conn, XCB_INPUT_FOCUS_POINTER_ROOT, + window->id, XCB_TIME_CURRENT_TIME); + } else { + xcb_set_input_focus (wm->conn, + XCB_INPUT_FOCUS_POINTER_ROOT, + XCB_NONE, + XCB_TIME_CURRENT_TIME); + } + + if (wm->focus_window) { + if (wm->focus_window->frame) + frame_unset_flag(wm->focus_window->frame, FRAME_FLAG_ACTIVE); + weston_wm_window_schedule_repaint(wm->focus_window); + } + wm->focus_window = window; + if (wm->focus_window) { + if (wm->focus_window->frame) + frame_set_flag(wm->focus_window->frame, FRAME_FLAG_ACTIVE); + weston_wm_window_schedule_repaint(wm->focus_window); + } +} + +static void +weston_wm_window_transform(struct wl_listener *listener, void *data) +{ + struct weston_surface *surface = data; + struct weston_wm_window *window = get_wm_window(surface); + struct weston_wm *wm = + container_of(listener, struct weston_wm, transform_listener); + uint32_t mask, values[2]; + + if (!window || !wm) + return; + + if (!window->view || !weston_view_is_mapped(window->view)) + return; + + if (window->x != window->view->geometry.x || + window->y != window->view->geometry.y) { + values[0] = window->view->geometry.x; + values[1] = window->view->geometry.y; + mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y; + + xcb_configure_window(wm->conn, window->frame_id, mask, values); + xcb_flush(wm->conn); + } +} + +#define ICCCM_WITHDRAWN_STATE 0 +#define ICCCM_NORMAL_STATE 1 +#define ICCCM_ICONIC_STATE 3 + +static void +weston_wm_window_set_wm_state(struct weston_wm_window *window, int32_t state) +{ + struct weston_wm *wm = window->wm; + uint32_t property[2]; + + property[0] = state; + property[1] = XCB_WINDOW_NONE; + + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + window->id, + wm->atom.wm_state, + wm->atom.wm_state, + 32, /* format */ + 2, property); +} + +static void +weston_wm_window_set_net_wm_state(struct weston_wm_window *window) +{ + struct weston_wm *wm = window->wm; + uint32_t property[1]; + int i; + + i = 0; + if (window->fullscreen) + property[i++] = wm->atom.net_wm_state_fullscreen; + + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + window->id, + wm->atom.net_wm_state, + XCB_ATOM_ATOM, + 32, /* format */ + i, property); +} + +static void +weston_wm_window_create_frame(struct weston_wm_window *window) +{ + struct weston_wm *wm = window->wm; + uint32_t values[3]; + int x, y, width, height; + + window->frame = frame_create(window->wm->theme, + window->width, window->height, + FRAME_BUTTON_CLOSE, window->name); + frame_resize_inside(window->frame, window->width, window->height); + + weston_wm_window_get_frame_size(window, &width, &height); + weston_wm_window_get_child_position(window, &x, &y); + + values[0] = wm->screen->black_pixel; + values[1] = + XCB_EVENT_MASK_KEY_PRESS | + XCB_EVENT_MASK_KEY_RELEASE | + XCB_EVENT_MASK_BUTTON_PRESS | + XCB_EVENT_MASK_BUTTON_RELEASE | + XCB_EVENT_MASK_POINTER_MOTION | + XCB_EVENT_MASK_ENTER_WINDOW | + XCB_EVENT_MASK_LEAVE_WINDOW | + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; + values[2] = wm->colormap; + + window->frame_id = xcb_generate_id(wm->conn); + xcb_create_window(wm->conn, + 32, + window->frame_id, + wm->screen->root, + 0, 0, + width, height, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + wm->visual_id, + XCB_CW_BORDER_PIXEL | + XCB_CW_EVENT_MASK | + XCB_CW_COLORMAP, values); + + xcb_reparent_window(wm->conn, window->id, window->frame_id, x, y); + + values[0] = 0; + xcb_configure_window(wm->conn, window->id, + XCB_CONFIG_WINDOW_BORDER_WIDTH, values); + + window->cairo_surface = + cairo_xcb_surface_create_with_xrender_format(wm->conn, + wm->screen, + window->frame_id, + &wm->format_rgba, + width, height); + + hash_table_insert(wm->window_hash, window->frame_id, window); +} + +static void +weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_map_request_event_t *map_request = + (xcb_map_request_event_t *) event; + struct weston_wm_window *window; + + if (our_resource(wm, map_request->window)) { + wm_log("XCB_MAP_REQUEST (window %d, ours)\n", + map_request->window); + return; + } + + window = hash_table_lookup(wm->window_hash, map_request->window); + + weston_wm_window_read_properties(window); + + if (window->frame_id == XCB_WINDOW_NONE) + weston_wm_window_create_frame(window); + + wm_log("XCB_MAP_REQUEST (window %d, %p, frame %d)\n", + window->id, window, window->frame_id); + + weston_wm_window_set_wm_state(window, ICCCM_NORMAL_STATE); + weston_wm_window_set_net_wm_state(window); + + xcb_map_window(wm->conn, map_request->window); + xcb_map_window(wm->conn, window->frame_id); +} + +static void +weston_wm_handle_map_notify(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_map_notify_event_t *map_notify = (xcb_map_notify_event_t *) event; + + if (our_resource(wm, map_notify->window)) { + wm_log("XCB_MAP_NOTIFY (window %d, ours)\n", + map_notify->window); + return; + } + + wm_log("XCB_MAP_NOTIFY (window %d)\n", map_notify->window); +} + +static void +weston_wm_handle_unmap_notify(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_unmap_notify_event_t *unmap_notify = + (xcb_unmap_notify_event_t *) event; + struct weston_wm_window *window; + + wm_log("XCB_UNMAP_NOTIFY (window %d, event %d%s)\n", + unmap_notify->window, + unmap_notify->event, + our_resource(wm, unmap_notify->window) ? ", ours" : ""); + + if (our_resource(wm, unmap_notify->window)) + return; + + if (unmap_notify->response_type & SEND_EVENT_MASK) + /* We just ignore the ICCCM 4.1.4 synthetic unmap notify + * as it may come in after we've destroyed the window. */ + return; + + window = hash_table_lookup(wm->window_hash, unmap_notify->window); + if (wm->focus_window == window) + wm->focus_window = NULL; + if (window->surface) + wl_list_remove(&window->surface_destroy_listener.link); + window->surface = NULL; + window->shsurf = NULL; + window->view = NULL; + xcb_unmap_window(wm->conn, window->frame_id); +} + +static void +weston_wm_window_draw_decoration(void *data) +{ + struct weston_wm_window *window = data; + struct weston_wm *wm = window->wm; + struct theme *t = wm->theme; + cairo_t *cr; + int x, y, width, height; + int32_t input_x, input_y, input_w, input_h; + + uint32_t flags = 0; + + weston_wm_window_read_properties(window); + + window->repaint_source = NULL; + + weston_wm_window_get_frame_size(window, &width, &height); + weston_wm_window_get_child_position(window, &x, &y); + + cairo_xcb_surface_set_size(window->cairo_surface, width, height); + cr = cairo_create(window->cairo_surface); + + if (window->fullscreen) { + /* nothing */ + } else if (window->decorate) { + if (wm->focus_window == window) + flags |= THEME_FRAME_ACTIVE; + + frame_repaint(window->frame, cr); + } else { + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba(cr, 0, 0, 0, 0); + cairo_paint(cr); + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_rgba(cr, 0, 0, 0, 0.45); + tile_mask(cr, t->shadow, 2, 2, width + 8, height + 8, 64, 64); + } + + cairo_destroy(cr); + + if (window->surface) { + pixman_region32_fini(&window->surface->pending.opaque); + if(window->has_alpha) { + pixman_region32_init(&window->surface->pending.opaque); + } else { + /* We leave an extra pixel around the X window area to + * make sure we don't sample from the undefined alpha + * channel when filtering. */ + pixman_region32_init_rect(&window->surface->pending.opaque, + x - 1, y - 1, + window->width + 2, + window->height + 2); + } + if (window->view) + weston_view_geometry_dirty(window->view); + + pixman_region32_fini(&window->surface->pending.input); + + if (window->fullscreen) { + input_x = 0; + input_y = 0; + input_w = window->width; + input_h = window->height; + } else if (window->decorate) { + frame_input_rect(window->frame, &input_x, &input_y, + &input_w, &input_h); + } + + pixman_region32_init_rect(&window->surface->pending.input, + input_x, input_y, input_w, input_h); + } +} + +static void +weston_wm_window_schedule_repaint(struct weston_wm_window *window) +{ + struct weston_wm *wm = window->wm; + int width, height; + + if (window->frame_id == XCB_WINDOW_NONE) { + if (window->surface != NULL) { + weston_wm_window_get_frame_size(window, &width, &height); + pixman_region32_fini(&window->surface->pending.opaque); + if(window->has_alpha) { + pixman_region32_init(&window->surface->pending.opaque); + } else { + pixman_region32_init_rect(&window->surface->pending.opaque, 0, 0, + width, height); + } + if (window->view) + weston_view_geometry_dirty(window->view); + } + return; + } + + if (window->repaint_source) + return; + + window->repaint_source = + wl_event_loop_add_idle(wm->server->loop, + weston_wm_window_draw_decoration, + window); +} + +static void +weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_property_notify_event_t *property_notify = + (xcb_property_notify_event_t *) event; + struct weston_wm_window *window; + + window = hash_table_lookup(wm->window_hash, property_notify->window); + if (!window) + return; + + window->properties_dirty = 1; + + wm_log("XCB_PROPERTY_NOTIFY: window %d, ", property_notify->window); + if (property_notify->state == XCB_PROPERTY_DELETE) + wm_log("deleted\n"); + else + read_and_dump_property(wm, property_notify->window, + property_notify->atom); + + if (property_notify->atom == wm->atom.net_wm_name || + property_notify->atom == XCB_ATOM_WM_NAME) + weston_wm_window_schedule_repaint(window); +} + +static void +weston_wm_window_create(struct weston_wm *wm, + xcb_window_t id, int width, int height, int x, int y, int override) +{ + struct weston_wm_window *window; + uint32_t values[1]; + xcb_get_geometry_cookie_t geometry_cookie; + xcb_get_geometry_reply_t *geometry_reply; + + window = zalloc(sizeof *window); + if (window == NULL) { + wm_log("failed to allocate window\n"); + return; + } + + geometry_cookie = xcb_get_geometry(wm->conn, id); + + values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE; + xcb_change_window_attributes(wm->conn, id, XCB_CW_EVENT_MASK, values); + + window->wm = wm; + window->id = id; + window->properties_dirty = 1; + window->override_redirect = override; + window->width = width; + window->height = height; + window->x = x; + window->y = y; + + geometry_reply = xcb_get_geometry_reply(wm->conn, geometry_cookie, NULL); + /* technically we should use XRender and check the visual format's + alpha_mask, but checking depth is simpler and works in all known cases */ + if(geometry_reply != NULL) + window->has_alpha = geometry_reply->depth == 32; + free(geometry_reply); + + hash_table_insert(wm->window_hash, id, window); +} + +static void +weston_wm_window_destroy(struct weston_wm_window *window) +{ + struct weston_wm *wm = window->wm; + + if (window->repaint_source) + wl_event_source_remove(window->repaint_source); + if (window->cairo_surface) + cairo_surface_destroy(window->cairo_surface); + + if (window->frame_id) { + xcb_reparent_window(wm->conn, window->id, wm->wm_window, 0, 0); + xcb_destroy_window(wm->conn, window->frame_id); + weston_wm_window_set_wm_state(window, ICCCM_WITHDRAWN_STATE); + hash_table_remove(wm->window_hash, window->frame_id); + window->frame_id = XCB_WINDOW_NONE; + } + + hash_table_remove(window->wm->window_hash, window->id); + free(window); +} + +static void +weston_wm_handle_create_notify(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_create_notify_event_t *create_notify = + (xcb_create_notify_event_t *) event; + + wm_log("XCB_CREATE_NOTIFY (window %d, width %d, height %d%s%s)\n", + create_notify->window, + create_notify->width, create_notify->height, + create_notify->override_redirect ? ", override" : "", + our_resource(wm, create_notify->window) ? ", ours" : ""); + + if (our_resource(wm, create_notify->window)) + return; + + weston_wm_window_create(wm, create_notify->window, + create_notify->width, create_notify->height, + create_notify->x, create_notify->y, + create_notify->override_redirect); +} + +static void +weston_wm_handle_destroy_notify(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_destroy_notify_event_t *destroy_notify = + (xcb_destroy_notify_event_t *) event; + struct weston_wm_window *window; + + wm_log("XCB_DESTROY_NOTIFY, win %d, event %d%s\n", + destroy_notify->window, + destroy_notify->event, + our_resource(wm, destroy_notify->window) ? ", ours" : ""); + + if (our_resource(wm, destroy_notify->window)) + return; + + window = hash_table_lookup(wm->window_hash, destroy_notify->window); + weston_wm_window_destroy(window); +} + +static void +weston_wm_handle_reparent_notify(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_reparent_notify_event_t *reparent_notify = + (xcb_reparent_notify_event_t *) event; + struct weston_wm_window *window; + + wm_log("XCB_REPARENT_NOTIFY (window %d, parent %d, event %d)\n", + reparent_notify->window, + reparent_notify->parent, + reparent_notify->event); + + if (reparent_notify->parent == wm->screen->root) { + weston_wm_window_create(wm, reparent_notify->window, 10, 10, + reparent_notify->x, reparent_notify->y, + reparent_notify->override_redirect); + } else if (!our_resource(wm, reparent_notify->parent)) { + window = hash_table_lookup(wm->window_hash, + reparent_notify->window); + weston_wm_window_destroy(window); + } +} + +struct weston_seat * +weston_wm_pick_seat(struct weston_wm *wm) +{ + return container_of(wm->server->compositor->seat_list.next, + struct weston_seat, link); +} + +static void +weston_wm_window_handle_moveresize(struct weston_wm_window *window, + xcb_client_message_event_t *client_message) +{ + static const int map[] = { + THEME_LOCATION_RESIZING_TOP_LEFT, + THEME_LOCATION_RESIZING_TOP, + THEME_LOCATION_RESIZING_TOP_RIGHT, + THEME_LOCATION_RESIZING_RIGHT, + THEME_LOCATION_RESIZING_BOTTOM_RIGHT, + THEME_LOCATION_RESIZING_BOTTOM, + THEME_LOCATION_RESIZING_BOTTOM_LEFT, + THEME_LOCATION_RESIZING_LEFT + }; + + struct weston_wm *wm = window->wm; + struct weston_seat *seat = weston_wm_pick_seat(wm); + int detail; + struct weston_shell_interface *shell_interface = + &wm->server->compositor->shell_interface; + + if (seat->pointer->button_count != 1 || !window->view + || seat->pointer->focus != window->view) + return; + + detail = client_message->data.data32[2]; + switch (detail) { + case _NET_WM_MOVERESIZE_MOVE: + shell_interface->move(window->shsurf, seat); + break; + case _NET_WM_MOVERESIZE_SIZE_TOPLEFT: + case _NET_WM_MOVERESIZE_SIZE_TOP: + case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT: + case _NET_WM_MOVERESIZE_SIZE_RIGHT: + case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT: + case _NET_WM_MOVERESIZE_SIZE_BOTTOM: + case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT: + case _NET_WM_MOVERESIZE_SIZE_LEFT: + shell_interface->resize(window->shsurf, seat, map[detail]); + break; + case _NET_WM_MOVERESIZE_CANCEL: + break; + } +} + +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 +#define _NET_WM_STATE_TOGGLE 2 + +static int +update_state(int action, int *state) +{ + int new_state, changed; + + switch (action) { + case _NET_WM_STATE_REMOVE: + new_state = 0; + break; + case _NET_WM_STATE_ADD: + new_state = 1; + break; + case _NET_WM_STATE_TOGGLE: + new_state = !*state; + break; + default: + return 0; + } + + changed = (*state != new_state); + *state = new_state; + + return changed; +} + +static void +weston_wm_window_configure(void *data); + +static void +weston_wm_window_handle_state(struct weston_wm_window *window, + xcb_client_message_event_t *client_message) +{ + struct weston_wm *wm = window->wm; + struct weston_shell_interface *shell_interface = + &wm->server->compositor->shell_interface; + uint32_t action, property; + + action = client_message->data.data32[0]; + property = client_message->data.data32[1]; + + if (property == wm->atom.net_wm_state_fullscreen && + update_state(action, &window->fullscreen)) { + weston_wm_window_set_net_wm_state(window); + if (window->fullscreen) { + window->saved_width = window->width; + window->saved_height = window->height; + + if (window->shsurf) + shell_interface->set_fullscreen(window->shsurf, + WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, + 0, NULL); + } else { + shell_interface->set_toplevel(window->shsurf); + window->width = window->saved_width; + window->height = window->saved_height; + if (window->frame) + frame_resize_inside(window->frame, + window->width, + window->height); + weston_wm_window_configure(window); + } + } +} + +static void +weston_wm_handle_client_message(struct weston_wm *wm, + xcb_generic_event_t *event) +{ + xcb_client_message_event_t *client_message = + (xcb_client_message_event_t *) event; + struct weston_wm_window *window; + + window = hash_table_lookup(wm->window_hash, client_message->window); + + wm_log("XCB_CLIENT_MESSAGE (%s %d %d %d %d %d win %d)\n", + get_atom_name(wm->conn, client_message->type), + client_message->data.data32[0], + client_message->data.data32[1], + client_message->data.data32[2], + client_message->data.data32[3], + client_message->data.data32[4], + client_message->window); + + if (client_message->type == wm->atom.net_wm_moveresize) + weston_wm_window_handle_moveresize(window, client_message); + else if (client_message->type == wm->atom.net_wm_state) + weston_wm_window_handle_state(window, client_message); +} + +enum cursor_type { + XWM_CURSOR_TOP, + XWM_CURSOR_BOTTOM, + XWM_CURSOR_LEFT, + XWM_CURSOR_RIGHT, + XWM_CURSOR_TOP_LEFT, + XWM_CURSOR_TOP_RIGHT, + XWM_CURSOR_BOTTOM_LEFT, + XWM_CURSOR_BOTTOM_RIGHT, + XWM_CURSOR_LEFT_PTR, +}; + +/* + * The following correspondences between file names and cursors was copied + * from: https://bugs.kde.org/attachment.cgi?id=67313 + */ + +static const char *bottom_left_corners[] = { + "bottom_left_corner", + "sw-resize", + "size_bdiag" +}; + +static const char *bottom_right_corners[] = { + "bottom_right_corner", + "se-resize", + "size_fdiag" +}; + +static const char *bottom_sides[] = { + "bottom_side", + "s-resize", + "size_ver" +}; + +static const char *left_ptrs[] = { + "left_ptr", + "default", + "top_left_arrow", + "left-arrow" +}; + +static const char *left_sides[] = { + "left_side", + "w-resize", + "size_hor" +}; + +static const char *right_sides[] = { + "right_side", + "e-resize", + "size_hor" +}; + +static const char *top_left_corners[] = { + "top_left_corner", + "nw-resize", + "size_fdiag" +}; + +static const char *top_right_corners[] = { + "top_right_corner", + "ne-resize", + "size_bdiag" +}; + +static const char *top_sides[] = { + "top_side", + "n-resize", + "size_ver" +}; + +struct cursor_alternatives { + const char **names; + size_t count; +}; + +static const struct cursor_alternatives cursors[] = { + {top_sides, ARRAY_LENGTH(top_sides)}, + {bottom_sides, ARRAY_LENGTH(bottom_sides)}, + {left_sides, ARRAY_LENGTH(left_sides)}, + {right_sides, ARRAY_LENGTH(right_sides)}, + {top_left_corners, ARRAY_LENGTH(top_left_corners)}, + {top_right_corners, ARRAY_LENGTH(top_right_corners)}, + {bottom_left_corners, ARRAY_LENGTH(bottom_left_corners)}, + {bottom_right_corners, ARRAY_LENGTH(bottom_right_corners)}, + {left_ptrs, ARRAY_LENGTH(left_ptrs)}, +}; + +static void +weston_wm_create_cursors(struct weston_wm *wm) +{ + const char *name; + int i, count = ARRAY_LENGTH(cursors); + size_t j; + + wm->cursors = malloc(count * sizeof(xcb_cursor_t)); + for (i = 0; i < count; i++) { + for (j = 0; j < cursors[i].count; j++) { + name = cursors[i].names[j]; + wm->cursors[i] = + xcb_cursor_library_load_cursor(wm, name); + if (wm->cursors[i] != (xcb_cursor_t)-1) + break; + } + } + + wm->last_cursor = -1; +} + +static void +weston_wm_destroy_cursors(struct weston_wm *wm) +{ + uint8_t i; + + for (i = 0; i < ARRAY_LENGTH(cursors); i++) + xcb_free_cursor(wm->conn, wm->cursors[i]); + + free(wm->cursors); +} + +static int +get_cursor_for_location(enum theme_location location) +{ + // int location = theme_get_location(t, x, y, width, height, 0); + + switch (location) { + case THEME_LOCATION_RESIZING_TOP: + return XWM_CURSOR_TOP; + case THEME_LOCATION_RESIZING_BOTTOM: + return XWM_CURSOR_BOTTOM; + case THEME_LOCATION_RESIZING_LEFT: + return XWM_CURSOR_LEFT; + case THEME_LOCATION_RESIZING_RIGHT: + return XWM_CURSOR_RIGHT; + case THEME_LOCATION_RESIZING_TOP_LEFT: + return XWM_CURSOR_TOP_LEFT; + case THEME_LOCATION_RESIZING_TOP_RIGHT: + return XWM_CURSOR_TOP_RIGHT; + case THEME_LOCATION_RESIZING_BOTTOM_LEFT: + return XWM_CURSOR_BOTTOM_LEFT; + case THEME_LOCATION_RESIZING_BOTTOM_RIGHT: + return XWM_CURSOR_BOTTOM_RIGHT; + case THEME_LOCATION_EXTERIOR: + case THEME_LOCATION_TITLEBAR: + default: + return XWM_CURSOR_LEFT_PTR; + } +} + +static void +weston_wm_window_set_cursor(struct weston_wm *wm, xcb_window_t window_id, + int cursor) +{ + uint32_t cursor_value_list; + + if (wm->last_cursor == cursor) + return; + + wm->last_cursor = cursor; + + cursor_value_list = wm->cursors[cursor]; + xcb_change_window_attributes (wm->conn, window_id, + XCB_CW_CURSOR, &cursor_value_list); + xcb_flush(wm->conn); +} + +static void +weston_wm_window_close(struct weston_wm_window *window, xcb_timestamp_t time) +{ + xcb_client_message_event_t client_message; + + if (window->delete_window) { + client_message.response_type = XCB_CLIENT_MESSAGE; + client_message.format = 32; + client_message.window = window->id; + client_message.type = window->wm->atom.wm_protocols; + client_message.data.data32[0] = + window->wm->atom.wm_delete_window; + client_message.data.data32[1] = time; + + xcb_send_event(window->wm->conn, 0, window->id, + XCB_EVENT_MASK_NO_EVENT, + (char *) &client_message); + } else { + xcb_kill_client(window->wm->conn, window->id); + } +} + +static void +weston_wm_handle_button(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_button_press_event_t *button = (xcb_button_press_event_t *) event; + struct weston_shell_interface *shell_interface = + &wm->server->compositor->shell_interface; + struct weston_seat *seat = weston_wm_pick_seat(wm); + struct weston_wm_window *window; + enum theme_location location; + enum frame_button_state button_state; + uint32_t button_id; + + wm_log("XCB_BUTTON_%s (detail %d)\n", + button->response_type == XCB_BUTTON_PRESS ? + "PRESS" : "RELEASE", button->detail); + + window = hash_table_lookup(wm->window_hash, button->event); + if (!window || !window->decorate) + return; + + if (button->detail != 1 && button->detail != 2) + return; + + button_state = button->response_type == XCB_BUTTON_PRESS ? + FRAME_BUTTON_PRESSED : FRAME_BUTTON_RELEASED; + button_id = button->detail == 1 ? BTN_LEFT : BTN_RIGHT; + + location = frame_pointer_button(window->frame, NULL, + button_id, button_state); + if (frame_status(window->frame) & FRAME_STATUS_REPAINT) + weston_wm_window_schedule_repaint(window); + + if (frame_status(window->frame) & FRAME_STATUS_MOVE) { + shell_interface->move(window->shsurf, seat); + frame_status_clear(window->frame, FRAME_STATUS_MOVE); + } + + if (frame_status(window->frame) & FRAME_STATUS_RESIZE) { + shell_interface->resize(window->shsurf, seat, location); + frame_status_clear(window->frame, FRAME_STATUS_RESIZE); + } + + if (frame_status(window->frame) & FRAME_STATUS_CLOSE) { + weston_wm_window_close(window, button->time); + frame_status_clear(window->frame, FRAME_STATUS_CLOSE); + } +} + +static void +weston_wm_handle_motion(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *) event; + struct weston_wm_window *window; + enum theme_location location; + int cursor; + + window = hash_table_lookup(wm->window_hash, motion->event); + if (!window || !window->decorate) + return; + + location = frame_pointer_motion(window->frame, NULL, + motion->event_x, motion->event_y); + if (frame_status(window->frame) & FRAME_STATUS_REPAINT) + weston_wm_window_schedule_repaint(window); + + cursor = get_cursor_for_location(location); + weston_wm_window_set_cursor(wm, window->frame_id, cursor); +} + +static void +weston_wm_handle_enter(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *) event; + struct weston_wm_window *window; + enum theme_location location; + int cursor; + + window = hash_table_lookup(wm->window_hash, enter->event); + if (!window || !window->decorate) + return; + + location = frame_pointer_enter(window->frame, NULL, + enter->event_x, enter->event_y); + if (frame_status(window->frame) & FRAME_STATUS_REPAINT) + weston_wm_window_schedule_repaint(window); + + cursor = get_cursor_for_location(location); + weston_wm_window_set_cursor(wm, window->frame_id, cursor); +} + +static void +weston_wm_handle_leave(struct weston_wm *wm, xcb_generic_event_t *event) +{ + xcb_leave_notify_event_t *leave = (xcb_leave_notify_event_t *) event; + struct weston_wm_window *window; + + window = hash_table_lookup(wm->window_hash, leave->event); + if (!window || !window->decorate) + return; + + frame_pointer_leave(window->frame, NULL); + if (frame_status(window->frame) & FRAME_STATUS_REPAINT) + weston_wm_window_schedule_repaint(window); + + weston_wm_window_set_cursor(wm, window->frame_id, XWM_CURSOR_LEFT_PTR); +} + +static int +weston_wm_handle_event(int fd, uint32_t mask, void *data) +{ + struct weston_wm *wm = data; + xcb_generic_event_t *event; + int count = 0; + + while (event = xcb_poll_for_event(wm->conn), event != NULL) { + if (weston_wm_handle_selection_event(wm, event)) { + free(event); + count++; + continue; + } + + if (weston_wm_handle_dnd_event(wm, event)) { + free(event); + count++; + continue; + } + + switch (EVENT_TYPE(event)) { + case XCB_BUTTON_PRESS: + case XCB_BUTTON_RELEASE: + weston_wm_handle_button(wm, event); + break; + case XCB_ENTER_NOTIFY: + weston_wm_handle_enter(wm, event); + break; + case XCB_LEAVE_NOTIFY: + weston_wm_handle_leave(wm, event); + break; + case XCB_MOTION_NOTIFY: + weston_wm_handle_motion(wm, event); + break; + case XCB_CREATE_NOTIFY: + weston_wm_handle_create_notify(wm, event); + break; + case XCB_MAP_REQUEST: + weston_wm_handle_map_request(wm, event); + break; + case XCB_MAP_NOTIFY: + weston_wm_handle_map_notify(wm, event); + break; + case XCB_UNMAP_NOTIFY: + weston_wm_handle_unmap_notify(wm, event); + break; + case XCB_REPARENT_NOTIFY: + weston_wm_handle_reparent_notify(wm, event); + break; + case XCB_CONFIGURE_REQUEST: + weston_wm_handle_configure_request(wm, event); + break; + case XCB_CONFIGURE_NOTIFY: + weston_wm_handle_configure_notify(wm, event); + break; + case XCB_DESTROY_NOTIFY: + weston_wm_handle_destroy_notify(wm, event); + break; + case XCB_MAPPING_NOTIFY: + wm_log("XCB_MAPPING_NOTIFY\n"); + break; + case XCB_PROPERTY_NOTIFY: + weston_wm_handle_property_notify(wm, event); + break; + case XCB_CLIENT_MESSAGE: + weston_wm_handle_client_message(wm, event); + break; + } + + free(event); + count++; + } + + xcb_flush(wm->conn); + + return count; +} + +static void +weston_wm_get_visual_and_colormap(struct weston_wm *wm) +{ + xcb_depth_iterator_t d_iter; + xcb_visualtype_iterator_t vt_iter; + xcb_visualtype_t *visualtype; + + d_iter = xcb_screen_allowed_depths_iterator(wm->screen); + visualtype = NULL; + while (d_iter.rem > 0) { + if (d_iter.data->depth == 32) { + vt_iter = xcb_depth_visuals_iterator(d_iter.data); + visualtype = vt_iter.data; + break; + } + + xcb_depth_next(&d_iter); + } + + if (visualtype == NULL) { + weston_log("no 32 bit visualtype\n"); + return; + } + + wm->visual_id = visualtype->visual_id; + wm->colormap = xcb_generate_id(wm->conn); + xcb_create_colormap(wm->conn, XCB_COLORMAP_ALLOC_NONE, + wm->colormap, wm->screen->root, wm->visual_id); +} + +static void +weston_wm_get_resources(struct weston_wm *wm) +{ + +#define F(field) offsetof(struct weston_wm, field) + + static const struct { const char *name; int offset; } atoms[] = { + { "WM_PROTOCOLS", F(atom.wm_protocols) }, + { "WM_NORMAL_HINTS", F(atom.wm_normal_hints) }, + { "WM_TAKE_FOCUS", F(atom.wm_take_focus) }, + { "WM_DELETE_WINDOW", F(atom.wm_delete_window) }, + { "WM_STATE", F(atom.wm_state) }, + { "WM_S0", F(atom.wm_s0) }, + { "WM_CLIENT_MACHINE", F(atom.wm_client_machine) }, + { "_NET_WM_CM_S0", F(atom.net_wm_cm_s0) }, + { "_NET_WM_NAME", F(atom.net_wm_name) }, + { "_NET_WM_PID", F(atom.net_wm_pid) }, + { "_NET_WM_ICON", F(atom.net_wm_icon) }, + { "_NET_WM_STATE", F(atom.net_wm_state) }, + { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) }, + { "_NET_WM_USER_TIME", F(atom.net_wm_user_time) }, + { "_NET_WM_ICON_NAME", F(atom.net_wm_icon_name) }, + { "_NET_WM_WINDOW_TYPE", F(atom.net_wm_window_type) }, + + { "_NET_WM_WINDOW_TYPE_DESKTOP", F(atom.net_wm_window_type_desktop) }, + { "_NET_WM_WINDOW_TYPE_DOCK", F(atom.net_wm_window_type_dock) }, + { "_NET_WM_WINDOW_TYPE_TOOLBAR", F(atom.net_wm_window_type_toolbar) }, + { "_NET_WM_WINDOW_TYPE_MENU", F(atom.net_wm_window_type_menu) }, + { "_NET_WM_WINDOW_TYPE_UTILITY", F(atom.net_wm_window_type_utility) }, + { "_NET_WM_WINDOW_TYPE_SPLASH", F(atom.net_wm_window_type_splash) }, + { "_NET_WM_WINDOW_TYPE_DIALOG", F(atom.net_wm_window_type_dialog) }, + { "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", F(atom.net_wm_window_type_dropdown) }, + { "_NET_WM_WINDOW_TYPE_POPUP_MENU", F(atom.net_wm_window_type_popup) }, + { "_NET_WM_WINDOW_TYPE_TOOLTIP", F(atom.net_wm_window_type_tooltip) }, + { "_NET_WM_WINDOW_TYPE_NOTIFICATION", F(atom.net_wm_window_type_notification) }, + { "_NET_WM_WINDOW_TYPE_COMBO", F(atom.net_wm_window_type_combo) }, + { "_NET_WM_WINDOW_TYPE_DND", F(atom.net_wm_window_type_dnd) }, + { "_NET_WM_WINDOW_TYPE_NORMAL", F(atom.net_wm_window_type_normal) }, + + { "_NET_WM_MOVERESIZE", F(atom.net_wm_moveresize) }, + { "_NET_SUPPORTING_WM_CHECK", + F(atom.net_supporting_wm_check) }, + { "_NET_SUPPORTED", F(atom.net_supported) }, + { "_MOTIF_WM_HINTS", F(atom.motif_wm_hints) }, + { "CLIPBOARD", F(atom.clipboard) }, + { "CLIPBOARD_MANAGER", F(atom.clipboard_manager) }, + { "TARGETS", F(atom.targets) }, + { "UTF8_STRING", F(atom.utf8_string) }, + { "_WL_SELECTION", F(atom.wl_selection) }, + { "INCR", F(atom.incr) }, + { "TIMESTAMP", F(atom.timestamp) }, + { "MULTIPLE", F(atom.multiple) }, + { "UTF8_STRING" , F(atom.utf8_string) }, + { "COMPOUND_TEXT", F(atom.compound_text) }, + { "TEXT", F(atom.text) }, + { "STRING", F(atom.string) }, + { "text/plain;charset=utf-8", F(atom.text_plain_utf8) }, + { "text/plain", F(atom.text_plain) }, + { "XdndSelection", F(atom.xdnd_selection) }, + { "XdndAware", F(atom.xdnd_aware) }, + { "XdndEnter", F(atom.xdnd_enter) }, + { "XdndLeave", F(atom.xdnd_leave) }, + { "XdndDrop", F(atom.xdnd_drop) }, + { "XdndStatus", F(atom.xdnd_status) }, + { "XdndFinished", F(atom.xdnd_finished) }, + { "XdndTypeList", F(atom.xdnd_type_list) }, + { "XdndActionCopy", F(atom.xdnd_action_copy) } + }; +#undef F + + xcb_xfixes_query_version_cookie_t xfixes_cookie; + xcb_xfixes_query_version_reply_t *xfixes_reply; + xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)]; + xcb_intern_atom_reply_t *reply; + xcb_render_query_pict_formats_reply_t *formats_reply; + xcb_render_query_pict_formats_cookie_t formats_cookie; + xcb_render_pictforminfo_t *formats; + uint32_t i; + + xcb_prefetch_extension_data (wm->conn, &xcb_xfixes_id); + xcb_prefetch_extension_data (wm->conn, &xcb_composite_id); + + formats_cookie = xcb_render_query_pict_formats(wm->conn); + + for (i = 0; i < ARRAY_LENGTH(atoms); i++) + cookies[i] = xcb_intern_atom (wm->conn, 0, + strlen(atoms[i].name), + atoms[i].name); + + for (i = 0; i < ARRAY_LENGTH(atoms); i++) { + reply = xcb_intern_atom_reply (wm->conn, cookies[i], NULL); + *(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom; + free(reply); + } + + wm->xfixes = xcb_get_extension_data(wm->conn, &xcb_xfixes_id); + if (!wm->xfixes || !wm->xfixes->present) + weston_log("xfixes not available\n"); + + xfixes_cookie = xcb_xfixes_query_version(wm->conn, + XCB_XFIXES_MAJOR_VERSION, + XCB_XFIXES_MINOR_VERSION); + xfixes_reply = xcb_xfixes_query_version_reply(wm->conn, + xfixes_cookie, NULL); + + weston_log("xfixes version: %d.%d\n", + xfixes_reply->major_version, xfixes_reply->minor_version); + + free(xfixes_reply); + + formats_reply = xcb_render_query_pict_formats_reply(wm->conn, + formats_cookie, 0); + if (formats_reply == NULL) + return; + + formats = xcb_render_query_pict_formats_formats(formats_reply); + for (i = 0; i < formats_reply->num_formats; i++) { + if (formats[i].direct.red_mask != 0xff && + formats[i].direct.red_shift != 16) + continue; + if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT && + formats[i].depth == 24) + wm->format_rgb = formats[i]; + if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT && + formats[i].depth == 32 && + formats[i].direct.alpha_mask == 0xff && + formats[i].direct.alpha_shift == 24) + wm->format_rgba = formats[i]; + } + + free(formats_reply); +} + +static void +weston_wm_create_wm_window(struct weston_wm *wm) +{ + static const char name[] = "Weston WM"; + + wm->wm_window = xcb_generate_id(wm->conn); + xcb_create_window(wm->conn, + XCB_COPY_FROM_PARENT, + wm->wm_window, + wm->screen->root, + 0, 0, + 10, 10, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + wm->screen->root_visual, + 0, NULL); + + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + wm->wm_window, + wm->atom.net_supporting_wm_check, + XCB_ATOM_WINDOW, + 32, /* format */ + 1, &wm->wm_window); + + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + wm->wm_window, + wm->atom.net_wm_name, + wm->atom.utf8_string, + 8, /* format */ + strlen(name), name); + + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + wm->screen->root, + wm->atom.net_supporting_wm_check, + XCB_ATOM_WINDOW, + 32, /* format */ + 1, &wm->wm_window); + + /* Claim the WM_S0 selection even though we don't suport + * the --replace functionality. */ + xcb_set_selection_owner(wm->conn, + wm->wm_window, + wm->atom.wm_s0, + XCB_TIME_CURRENT_TIME); + + xcb_set_selection_owner(wm->conn, + wm->wm_window, + wm->atom.net_wm_cm_s0, + XCB_TIME_CURRENT_TIME); +} + +struct weston_wm * +weston_wm_create(struct weston_xserver *wxs) +{ + struct weston_wm *wm; + struct wl_event_loop *loop; + xcb_screen_iterator_t s; + uint32_t values[1]; + int sv[2]; + xcb_atom_t supported[3]; + + wm = zalloc(sizeof *wm); + if (wm == NULL) + return NULL; + + wm->server = wxs; + wm->window_hash = hash_table_create(); + if (wm->window_hash == NULL) { + free(wm); + return NULL; + } + + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { + weston_log("socketpair failed\n"); + hash_table_destroy(wm->window_hash); + free(wm); + return NULL; + } + + xserver_send_client(wxs->resource, sv[1]); + wl_client_flush(wl_resource_get_client(wxs->resource)); + close(sv[1]); + + /* xcb_connect_to_fd takes ownership of the fd. */ + wm->conn = xcb_connect_to_fd(sv[0], NULL); + if (xcb_connection_has_error(wm->conn)) { + weston_log("xcb_connect_to_fd failed\n"); + close(sv[0]); + hash_table_destroy(wm->window_hash); + free(wm); + return NULL; + } + + s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn)); + wm->screen = s.data; + + loop = wl_display_get_event_loop(wxs->wl_display); + wm->source = + wl_event_loop_add_fd(loop, sv[0], + WL_EVENT_READABLE, + weston_wm_handle_event, wm); + wl_event_source_check(wm->source); + + weston_wm_get_resources(wm); + weston_wm_get_visual_and_colormap(wm); + + values[0] = + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | + XCB_EVENT_MASK_PROPERTY_CHANGE; + xcb_change_window_attributes(wm->conn, wm->screen->root, + XCB_CW_EVENT_MASK, values); + + xcb_composite_redirect_subwindows(wm->conn, wm->screen->root, + XCB_COMPOSITE_REDIRECT_MANUAL); + + wm->theme = theme_create(); + + weston_wm_create_wm_window(wm); + + supported[0] = wm->atom.net_wm_moveresize; + supported[1] = wm->atom.net_wm_state; + supported[2] = wm->atom.net_wm_state_fullscreen; + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + wm->screen->root, + wm->atom.net_supported, + XCB_ATOM_ATOM, + 32, /* format */ + ARRAY_LENGTH(supported), supported); + + weston_wm_selection_init(wm); + + weston_wm_dnd_init(wm); + + xcb_flush(wm->conn); + + wm->activate_listener.notify = weston_wm_window_activate; + wl_signal_add(&wxs->compositor->activate_signal, + &wm->activate_listener); + wm->transform_listener.notify = weston_wm_window_transform; + wl_signal_add(&wxs->compositor->transform_signal, + &wm->transform_listener); + wm->kill_listener.notify = weston_wm_kill_client; + wl_signal_add(&wxs->compositor->kill_signal, + &wm->kill_listener); + + weston_wm_create_cursors(wm); + weston_wm_window_set_cursor(wm, wm->screen->root, XWM_CURSOR_LEFT_PTR); + + weston_log("created wm\n"); + + return wm; +} + +void +weston_wm_destroy(struct weston_wm *wm) +{ + /* FIXME: Free windows in hash. */ + hash_table_destroy(wm->window_hash); + weston_wm_destroy_cursors(wm); + xcb_disconnect(wm->conn); + wl_event_source_remove(wm->source); + wl_list_remove(&wm->selection_listener.link); + wl_list_remove(&wm->activate_listener.link); + wl_list_remove(&wm->kill_listener.link); + wl_list_remove(&wm->transform_listener.link); + + free(wm); +} + +static void +surface_destroy(struct wl_listener *listener, void *data) +{ + struct weston_wm_window *window = + container_of(listener, + struct weston_wm_window, surface_destroy_listener); + + wm_log("surface for xid %d destroyed\n", window->id); + + /* This should have been freed by the shell. + Don't try to use it later. */ + window->shsurf = NULL; + window->surface = NULL; + window->view = NULL; +} + +static struct weston_wm_window * +get_wm_window(struct weston_surface *surface) +{ + struct wl_listener *listener; + + listener = wl_signal_get(&surface->destroy_signal, surface_destroy); + if (listener) + return container_of(listener, struct weston_wm_window, + surface_destroy_listener); + + return NULL; +} + +static void +weston_wm_window_configure(void *data) +{ + struct weston_wm_window *window = data; + struct weston_wm *wm = window->wm; + uint32_t values[4]; + int x, y, width, height; + + weston_wm_window_get_child_position(window, &x, &y); + values[0] = x; + values[1] = y; + values[2] = window->width; + values[3] = window->height; + xcb_configure_window(wm->conn, + window->id, + XCB_CONFIG_WINDOW_X | + XCB_CONFIG_WINDOW_Y | + XCB_CONFIG_WINDOW_WIDTH | + XCB_CONFIG_WINDOW_HEIGHT, + values); + + weston_wm_window_get_frame_size(window, &width, &height); + values[0] = width; + values[1] = height; + xcb_configure_window(wm->conn, + window->frame_id, + XCB_CONFIG_WINDOW_WIDTH | + XCB_CONFIG_WINDOW_HEIGHT, + values); + + window->configure_source = NULL; + + weston_wm_window_schedule_repaint(window); +} + +static void +send_configure(struct weston_surface *surface, + uint32_t edges, int32_t width, int32_t height) +{ + struct weston_wm_window *window = get_wm_window(surface); + struct weston_wm *wm = window->wm; + struct theme *t = window->wm->theme; + int vborder, hborder; + + if (window->fullscreen) { + hborder = 0; + vborder = 0; + } else if (window->decorate) { + hborder = 2 * (t->margin + t->width); + vborder = 2 * t->margin + t->titlebar_height + t->width; + } else { + hborder = 2 * t->margin; + vborder = 2 * t->margin; + } + + if (width > hborder) + window->width = width - hborder; + else + window->width = 1; + + if (height > vborder) + window->height = height - vborder; + else + window->height = 1; + + if (window->frame) + frame_resize_inside(window->frame, window->width, window->height); + + if (window->configure_source) + return; + + window->configure_source = + wl_event_loop_add_idle(wm->server->loop, + weston_wm_window_configure, window); +} + +static const struct weston_shell_client shell_client = { + send_configure +}; + +static int +legacy_fullscreen(struct weston_wm *wm, + struct weston_wm_window *window, + struct weston_output **output_ret) +{ + struct weston_compositor *compositor = wm->server->compositor; + struct weston_output *output; + uint32_t minmax = PMinSize | PMaxSize; + int matching_size; + + /* Heuristics for detecting legacy fullscreen windows... */ + + wl_list_for_each(output, &compositor->output_list, link) { + if (output->x == window->x && + output->y == window->y && + output->width == window->width && + output->height == window->height && + window->override_redirect) { + *output_ret = output; + return 1; + } + + matching_size = 0; + if ((window->size_hints.flags & (USSize |PSize)) && + window->size_hints.width == output->width && + window->size_hints.height == output->height) + matching_size = 1; + if ((window->size_hints.flags & minmax) == minmax && + window->size_hints.min_width == output->width && + window->size_hints.min_height == output->height && + window->size_hints.max_width == output->width && + window->size_hints.max_height == output->height) + matching_size = 1; + + if (matching_size && !window->decorate && + (window->size_hints.flags & (USPosition | PPosition)) && + window->size_hints.x == output->x && + window->size_hints.y == output->y) { + *output_ret = output; + return 1; + } + } + + return 0; +} +static void +xserver_map_shell_surface(struct weston_wm *wm, + struct weston_wm_window *window) +{ + struct weston_shell_interface *shell_interface = + &wm->server->compositor->shell_interface; + struct weston_output *output; + struct weston_wm_window *parent; + + if (!shell_interface->create_shell_surface) + return; + + if (!shell_interface->get_primary_view) + return; + + window->shsurf = + shell_interface->create_shell_surface(shell_interface->shell, + window->surface, + &shell_client); + window->view = shell_interface->get_primary_view(shell_interface->shell, + window->shsurf); + + if (window->name) + shell_interface->set_title(window->shsurf, window->name); + + if (window->fullscreen) { + window->saved_width = window->width; + window->saved_height = window->height; + shell_interface->set_fullscreen(window->shsurf, + WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, + 0, NULL); + return; + } else if (legacy_fullscreen(wm, window, &output)) { + window->fullscreen = 1; + shell_interface->set_fullscreen(window->shsurf, + WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, + 0, output); + } else if (window->override_redirect) { + shell_interface->set_xwayland(window->shsurf, + window->x, + window->y, + WL_SHELL_SURFACE_TRANSIENT_INACTIVE); + } else if (window->transient_for) { + parent = window->transient_for; + shell_interface->set_transient(window->shsurf, + parent->surface, + parent->x - window->x, + parent->y - window->y, 0); + } else { + shell_interface->set_toplevel(window->shsurf); + } +} + +static void +xserver_set_window_id(struct wl_client *client, struct wl_resource *resource, + struct wl_resource *surface_resource, uint32_t id) +{ + struct weston_xserver *wxs = wl_resource_get_user_data(resource); + struct weston_wm *wm = wxs->wm; + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + struct weston_wm_window *window; + + if (client != wxs->client) + return; + + window = hash_table_lookup(wm->window_hash, id); + if (window == NULL) { + weston_log("set_window_id for unknown window %d\n", id); + return; + } + + wm_log("set_window_id %d for surface %p\n", id, surface); + + weston_wm_window_read_properties(window); + + /* A weston_wm_window may have many different surfaces assigned + * throughout its life, so we must make sure to remove the listener + * from the old surface signal list. */ + if (window->surface) + wl_list_remove(&window->surface_destroy_listener.link); + + window->surface = (struct weston_surface *) surface; + window->surface_destroy_listener.notify = surface_destroy; + wl_signal_add(&surface->destroy_signal, + &window->surface_destroy_listener); + + weston_wm_window_schedule_repaint(window); + xserver_map_shell_surface(wm, window); +} + +const struct xserver_interface xserver_implementation = { + xserver_set_window_id +}; diff --git a/xwayland/xwayland.h b/xwayland/xwayland.h new file mode 100644 index 00000000..c684cc51 --- /dev/null +++ b/xwayland/xwayland.h @@ -0,0 +1,175 @@ +/* + * Copyright © 2012 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "compositor.h" + +#define SEND_EVENT_MASK (0x80) +#define EVENT_TYPE(event) ((event)->response_type & ~SEND_EVENT_MASK) + +struct weston_xserver { + struct wl_display *wl_display; + struct wl_event_loop *loop; + struct wl_event_source *sigchld_source; + int abstract_fd; + struct wl_event_source *abstract_source; + int unix_fd; + struct wl_event_source *unix_source; + int display; + struct weston_process process; + struct wl_resource *resource; + struct wl_client *client; + struct weston_compositor *compositor; + struct weston_wm *wm; + struct wl_listener destroy_listener; +}; + +struct weston_wm { + xcb_connection_t *conn; + const xcb_query_extension_reply_t *xfixes; + struct wl_event_source *source; + xcb_screen_t *screen; + struct hash_table *window_hash; + struct weston_xserver *server; + xcb_window_t wm_window; + struct weston_wm_window *focus_window; + struct theme *theme; + xcb_cursor_t *cursors; + int last_cursor; + xcb_render_pictforminfo_t format_rgb, format_rgba; + xcb_visualid_t visual_id; + xcb_colormap_t colormap; + struct wl_listener activate_listener; + struct wl_listener transform_listener; + struct wl_listener kill_listener; + + xcb_window_t selection_window; + xcb_window_t selection_owner; + int incr; + int data_source_fd; + struct wl_event_source *property_source; + xcb_get_property_reply_t *property_reply; + int property_start; + struct wl_array source_data; + xcb_selection_request_event_t selection_request; + xcb_atom_t selection_target; + xcb_timestamp_t selection_timestamp; + int selection_property_set; + int flush_property_on_delete; + struct wl_listener selection_listener; + + xcb_window_t dnd_window; + xcb_window_t dnd_owner; + + struct { + xcb_atom_t wm_protocols; + xcb_atom_t wm_normal_hints; + xcb_atom_t wm_take_focus; + xcb_atom_t wm_delete_window; + xcb_atom_t wm_state; + xcb_atom_t wm_s0; + xcb_atom_t wm_client_machine; + xcb_atom_t net_wm_cm_s0; + xcb_atom_t net_wm_name; + xcb_atom_t net_wm_pid; + xcb_atom_t net_wm_icon; + xcb_atom_t net_wm_state; + xcb_atom_t net_wm_state_fullscreen; + xcb_atom_t net_wm_user_time; + xcb_atom_t net_wm_icon_name; + xcb_atom_t net_wm_window_type; + xcb_atom_t net_wm_window_type_desktop; + xcb_atom_t net_wm_window_type_dock; + xcb_atom_t net_wm_window_type_toolbar; + xcb_atom_t net_wm_window_type_menu; + xcb_atom_t net_wm_window_type_utility; + xcb_atom_t net_wm_window_type_splash; + xcb_atom_t net_wm_window_type_dialog; + xcb_atom_t net_wm_window_type_dropdown; + xcb_atom_t net_wm_window_type_popup; + xcb_atom_t net_wm_window_type_tooltip; + xcb_atom_t net_wm_window_type_notification; + xcb_atom_t net_wm_window_type_combo; + xcb_atom_t net_wm_window_type_dnd; + xcb_atom_t net_wm_window_type_normal; + xcb_atom_t net_wm_moveresize; + xcb_atom_t net_supporting_wm_check; + xcb_atom_t net_supported; + xcb_atom_t motif_wm_hints; + xcb_atom_t clipboard; + xcb_atom_t clipboard_manager; + xcb_atom_t targets; + xcb_atom_t utf8_string; + xcb_atom_t wl_selection; + xcb_atom_t incr; + xcb_atom_t timestamp; + xcb_atom_t multiple; + xcb_atom_t compound_text; + xcb_atom_t text; + xcb_atom_t string; + xcb_atom_t text_plain_utf8; + xcb_atom_t text_plain; + xcb_atom_t xdnd_selection; + xcb_atom_t xdnd_aware; + xcb_atom_t xdnd_enter; + xcb_atom_t xdnd_leave; + xcb_atom_t xdnd_drop; + xcb_atom_t xdnd_status; + xcb_atom_t xdnd_finished; + xcb_atom_t xdnd_type_list; + xcb_atom_t xdnd_action_copy; + } atom; +}; + +void +dump_property(struct weston_wm *wm, xcb_atom_t property, + xcb_get_property_reply_t *reply); + +const char * +get_atom_name(xcb_connection_t *c, xcb_atom_t atom); + +void +weston_wm_selection_init(struct weston_wm *wm); +int +weston_wm_handle_selection_event(struct weston_wm *wm, + xcb_generic_event_t *event); + +extern const struct xserver_interface xserver_implementation; + +struct weston_wm * +weston_wm_create(struct weston_xserver *wxs); +void +weston_wm_destroy(struct weston_wm *wm); + +struct weston_seat * +weston_wm_pick_seat(struct weston_wm *wm); + +int +weston_wm_handle_dnd_event(struct weston_wm *wm, + xcb_generic_event_t *event); +void +weston_wm_dnd_init(struct weston_wm *wm); -- cgit v1.2.1