X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=clients%2Fwindow.c;h=e44d65c9249ffe325b33df258b7bf190c6477631;hb=b2c18647775732da740946eb199b1f2b46ba950b;hp=0afd46bad920d3fdb36ef07973355502bd568596;hpb=01eaaac79af284a37b61dbf8b2730c7fe26c461e;p=profile%2Fivi%2Fweston-ivi-shell.git diff --git a/clients/window.c b/clients/window.c index 0afd46b..e44d65c 100644 --- a/clients/window.c +++ b/clients/window.c @@ -65,6 +65,7 @@ typedef void *EGLContext; #include #include #include "../shared/cairo-util.h" +#include "xdg-shell-client-protocol.h" #include "text-cursor-position-client-protocol.h" #include "workspaces-client-protocol.h" #include "../shared/os-compatibility.h" @@ -85,11 +86,11 @@ struct display { struct wl_registry *registry; struct wl_compositor *compositor; struct wl_subcompositor *subcompositor; - struct wl_shell *shell; struct wl_shm *shm; struct wl_data_device_manager *data_device_manager; struct text_cursor_position *text_cursor_position; struct workspace_manager *workspace_manager; + struct xdg_shell *xdg_shell; EGLDisplay dpy; EGLConfig argb_config; EGLContext argb_ctx; @@ -134,16 +135,6 @@ struct display { int seat_version; }; -enum { - TYPE_NONE, - TYPE_TOPLEVEL, - TYPE_FULLSCREEN, - TYPE_MAXIMIZED, - TYPE_TRANSIENT, - TYPE_MENU, - TYPE_CUSTOM -}; - struct window_output { struct output *output; struct wl_list link; @@ -221,25 +212,24 @@ struct surface { struct window { struct display *display; - struct window *parent; struct wl_list window_output_list; char *title; struct rectangle saved_allocation; struct rectangle min_allocation; struct rectangle pending_allocation; + struct rectangle last_geometry; int x, y; - int resize_edges; int redraw_needed; int redraw_task_scheduled; struct task redraw_task; int resize_needed; - int saved_type; - int type; - int focus_count; + int custom; + int focused; int resizing; - int fullscreen_method; - int configure_requests; + + int fullscreen; + int maximized; enum preferred_format preferred_format; @@ -250,9 +240,14 @@ struct window { window_close_handler_t close_handler; window_fullscreen_handler_t fullscreen_handler; window_output_handler_t output_handler; + window_state_changed_handler_t state_changed_handler; struct surface *main_surface; - struct wl_shell_surface *shell_surface; + struct xdg_surface *xdg_surface; + struct xdg_popup *xdg_popup; + + struct window *parent; + struct wl_surface *last_parent_surface; struct window_frame *frame; @@ -286,10 +281,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; }; @@ -321,6 +322,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; @@ -330,6 +336,11 @@ struct input { xkb_mod_mask_t shift_mask; } xkb; + int32_t repeat_rate_sec; + int32_t repeat_rate_nsec; + int32_t repeat_delay_sec; + int32_t repeat_delay_nsec; + struct task repeat_task; int repeat_timer_fd; uint32_t repeat_sym; @@ -345,6 +356,8 @@ struct output { struct wl_list link; int transform; int scale; + char *make; + char *model; display_output_handler_t destroy_handler; void *user_data; @@ -354,9 +367,13 @@ struct window_frame { struct widget *widget; struct widget *child; struct frame *frame; + + uint32_t last_time; + uint32_t did_double, double_click; }; struct menu { + void *user_data; struct window *window; struct widget *widget; struct input *input; @@ -371,7 +388,6 @@ struct menu { struct tooltip { struct widget *parent; - struct window *window; struct widget *widget; char *entry; struct task tooltip_task; @@ -391,22 +407,6 @@ enum { CURSOR_UNSET }; -enum window_location { - WINDOW_INTERIOR = 0, - WINDOW_RESIZING_TOP = 1, - WINDOW_RESIZING_BOTTOM = 2, - WINDOW_RESIZING_LEFT = 4, - WINDOW_RESIZING_TOP_LEFT = 5, - WINDOW_RESIZING_BOTTOM_LEFT = 6, - WINDOW_RESIZING_RIGHT = 8, - WINDOW_RESIZING_TOP_RIGHT = 9, - WINDOW_RESIZING_BOTTOM_RIGHT = 10, - WINDOW_RESIZING_MASK = 15, - WINDOW_EXTERIOR = 16, - WINDOW_TITLEBAR = 17, - WINDOW_CLIENT_AREA = 18, -}; - static const cairo_user_data_key_t shm_surface_data_key; /* #define DEBUG */ @@ -581,8 +581,8 @@ egl_window_surface_release(struct toysurface *base) if (!device) return; - if (!eglMakeCurrent(surface->display->dpy, NULL, NULL, - surface->display->argb_ctx)) + if (!eglMakeCurrent(surface->display->dpy, + EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) fprintf(stderr, "failed to make context current\n"); cairo_device_release(device); @@ -1078,6 +1078,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); @@ -1289,6 +1292,10 @@ create_cursors(struct display *display) weston_config_destroy(config); display->cursor_theme = wl_cursor_theme_load(theme, size, display->shm); + if (!display->cursor_theme) { + fprintf(stderr, "could not load theme '%s'\n", theme); + return; + } free(theme); display->cursors = xmalloc(ARRAY_LENGTH(cursors) * sizeof display->cursors[0]); @@ -1353,28 +1360,16 @@ surface_flush(struct surface *surface) int window_has_focus(struct window *window) { - return window->focus_count > 0; + return window->focused; } static void -window_flush(struct window *window) +window_close(struct window *window) { - struct surface *surface; - - if (window->type == TYPE_NONE) { - window->type = TYPE_TOPLEVEL; - if (window->shell_surface) - wl_shell_surface_set_toplevel(window->shell_surface); - } - - wl_list_for_each(surface, &window->subsurface_list, link) { - if (surface == window->main_surface) - continue; - - surface_flush(surface); - } - - surface_flush(window->main_surface); + if (window->close_handler) + window->close_handler(window->user_data); + else + display_exit(window->display); } struct display * @@ -1384,7 +1379,7 @@ window_get_display(struct window *window) } static void -surface_create_surface(struct surface *surface, int dx, int dy, uint32_t flags) +surface_create_surface(struct surface *surface, uint32_t flags) { struct display *display = surface->window->display; struct rectangle allocation = surface->allocation; @@ -1404,7 +1399,7 @@ surface_create_surface(struct surface *surface, int dx, int dy, uint32_t flags) flags, &allocation); surface->cairo_surface = surface->toysurface->prepare( - surface->toysurface, dx, dy, + surface->toysurface, 0, 0, allocation.width, allocation.height, flags, surface->buffer_transform, surface->buffer_scale); } @@ -1414,8 +1409,6 @@ window_create_main_surface(struct window *window) { struct surface *surface = window->main_surface; uint32_t flags = 0; - int dx = 0; - int dy = 0; if (window->resizing) flags |= SURFACE_HINT_RESIZE; @@ -1423,17 +1416,7 @@ window_create_main_surface(struct window *window) if (window->preferred_format == WINDOW_PREFERRED_FORMAT_RGB565) flags |= SURFACE_HINT_RGB565; - if (window->resize_edges & WINDOW_RESIZING_LEFT) - dx = surface->server_allocation.width - - surface->allocation.width; - - if (window->resize_edges & WINDOW_RESIZING_TOP) - dy = surface->server_allocation.height - - surface->allocation.height; - - window->resize_edges = 0; - - surface_create_surface(surface, dx, dy, flags); + surface_create_surface(surface, flags); } int @@ -1538,8 +1521,10 @@ window_destroy(struct window *window) if (window->frame) window_frame_destroy(window->frame); - if (window->shell_surface) - wl_shell_surface_destroy(window->shell_surface); + if (window->xdg_surface) + xdg_surface_destroy(window->xdg_surface); + if (window->xdg_popup) + xdg_popup_destroy(window->xdg_popup); surface_destroy(window->main_surface); @@ -1600,6 +1585,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; } @@ -1638,10 +1624,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) @@ -1698,11 +1682,13 @@ 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); else - surface_create_surface(surface, 0, 0, 0); + surface_create_surface(surface, 0); } return surface->cairo_surface; @@ -1810,6 +1796,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) { @@ -1924,6 +1916,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) { @@ -1940,12 +1939,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) { @@ -1955,6 +1948,7 @@ tooltip_redraw_handler(struct widget *widget, void *data) int32_t width, height; cr = widget_cairo_create(widget); + cairo_translate(cr, widget->allocation.x, widget->allocation.y); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); cairo_paint(cr); @@ -1974,7 +1968,7 @@ tooltip_redraw_handler(struct widget *widget, void *data) } static cairo_text_extents_t -get_text_extents(struct tooltip *tooltip) +get_text_extents(struct display *display, struct tooltip *tooltip) { cairo_t *cr; cairo_text_extents_t extents; @@ -1983,7 +1977,7 @@ get_text_extents(struct tooltip *tooltip) * created yet, and parent does not have a valid surface * outside repaint, either. */ - cr = cairo_create(tooltip->window->display->dummy_surface); + cr = cairo_create(display->dummy_surface); cairo_text_extents(cr, tooltip->entry, &extents); cairo_destroy(cr); @@ -1995,7 +1989,6 @@ window_create_tooltip(struct tooltip *tooltip) { struct widget *parent = tooltip->parent; struct display *display = parent->window->display; - struct window *window; const int offset_y = 27; const int margin = 3; cairo_text_extents_t extents; @@ -2003,18 +1996,13 @@ window_create_tooltip(struct tooltip *tooltip) if (tooltip->widget) return 0; - window = window_create_transient(display, parent->window, tooltip->x, - tooltip->y + offset_y, - WL_SHELL_SURFACE_TRANSIENT_INACTIVE); - if (!window) - return -1; + tooltip->widget = window_add_subsurface(parent->window, tooltip, SUBSURFACE_DESYNCHRONIZED); - tooltip->window = window; - tooltip->widget = window_add_widget(tooltip->window, tooltip); - - extents = get_text_extents(tooltip); + extents = get_text_extents(display, tooltip); widget_set_redraw_handler(tooltip->widget, tooltip_redraw_handler); - window_schedule_resize(window, extents.width + 20, 20 + margin * 2); + widget_set_allocation(tooltip->widget, + tooltip->x, tooltip->y + offset_y, + extents.width + 20, 20 + margin * 2); return 0; } @@ -2030,9 +2018,7 @@ widget_destroy_tooltip(struct widget *parent) if (tooltip->widget) { widget_destroy(tooltip->widget); - window_destroy(tooltip->window); tooltip->widget = NULL; - tooltip->window = NULL; } close(tooltip->tooltip_fd); @@ -2096,7 +2082,6 @@ widget_set_tooltip(struct widget *parent, char *entry, float x, float y) parent->tooltip = tooltip; tooltip->parent = parent; tooltip->widget = NULL; - tooltip->window = NULL; tooltip->x = x; tooltip->y = y; tooltip->entry = strdup(entry); @@ -2140,18 +2125,12 @@ frame_resize_handler(struct widget *widget, struct rectangle input; struct rectangle opaque; - if (widget->window->type == TYPE_FULLSCREEN) { + if (widget->window->fullscreen) { interior.x = 0; interior.y = 0; interior.width = width; interior.height = height; } else { - if (widget->window->type == TYPE_MAXIMIZED) { - frame_set_flag(frame->frame, FRAME_FLAG_MAXIMIZED); - } else { - frame_unset_flag(frame->frame, FRAME_FLAG_MAXIMIZED); - } - frame_resize(frame->frame, width, height); frame_interior(frame->frame, &interior.x, &interior.y, &interior.width, &interior.height); @@ -2164,7 +2143,7 @@ frame_resize_handler(struct widget *widget, child->resize_handler(child, interior.width, interior.height, child->user_data); - if (widget->window->type == TYPE_FULLSCREEN) { + if (widget->window->fullscreen) { width = child->allocation.width; height = child->allocation.height; } else { @@ -2180,7 +2159,7 @@ frame_resize_handler(struct widget *widget, widget->surface->input_region = wl_compositor_create_region(widget->window->display->compositor); - if (widget->window->type != TYPE_FULLSCREEN) { + if (!widget->window->fullscreen) { frame_input_rect(frame->frame, &input.x, &input.y, &input.width, &input.height); wl_region_add(widget->surface->input_region, @@ -2192,7 +2171,7 @@ frame_resize_handler(struct widget *widget, widget_set_allocation(widget, 0, 0, width, height); if (child->opaque) { - if (widget->window->type != TYPE_FULLSCREEN) { + if (!widget->window->fullscreen) { frame_opaque_rect(frame->frame, &opaque.x, &opaque.y, &opaque.width, &opaque.height); @@ -2216,15 +2195,9 @@ frame_redraw_handler(struct widget *widget, void *data) struct window_frame *frame = data; struct window *window = widget->window; - if (window->type == TYPE_FULLSCREEN) + if (window->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); frame_repaint(frame->frame, cr); @@ -2238,7 +2211,7 @@ frame_get_pointer_image_for_location(struct window_frame *frame, { struct window *window = frame->widget->window; - if (window->type != TYPE_TOPLEVEL) + if (window->custom) return CURSOR_LEFT_PTR; switch (location) { @@ -2266,18 +2239,14 @@ frame_get_pointer_image_for_location(struct window_frame *frame, } static void -frame_menu_func(struct window *window, - struct input *input, int index, void *data) +frame_menu_func(void *data, struct input *input, int index) { + struct window *window = data; struct display *display; switch (index) { case 0: /* close */ - if (window->close_handler) - window->close_handler(window->parent, - window->user_data); - else - display_exit(window->display); + window_close(window); break; case 1: /* move to workspace above */ display = window->display; @@ -2377,8 +2346,10 @@ frame_handle_status(struct window_frame *frame, struct input *input, if (status & FRAME_STATUS_REPAINT) widget_schedule_redraw(frame->widget); - if (status & FRAME_STATUS_MINIMIZE) - fprintf(stderr,"Minimize stub\n"); + if (status & FRAME_STATUS_MINIMIZE) { + window_set_minimized(window); + frame_status_clear(frame->frame, FRAME_STATUS_MINIMIZE); + } if (status & FRAME_STATUS_MENU) { window_show_frame_menu(window, input, time); @@ -2386,41 +2357,37 @@ frame_handle_status(struct window_frame *frame, struct input *input, } if (status & FRAME_STATUS_MAXIMIZE) { - window_set_maximized(window, window->type != TYPE_MAXIMIZED); + window_set_maximized(window, !window->maximized); frame_status_clear(frame->frame, FRAME_STATUS_MAXIMIZE); } if (status & FRAME_STATUS_CLOSE) { - if (window->close_handler) - window->close_handler(window->parent, - window->user_data); - else - display_exit(window->display); + window_close(window); return; } - if ((status & FRAME_STATUS_MOVE) && window->shell_surface) { + if ((status & FRAME_STATUS_MOVE) && window->xdg_surface) { input_ungrab(input); - wl_shell_surface_move(window->shell_surface, - input_get_seat(input), - window->display->serial); + xdg_surface_move(window->xdg_surface, + input_get_seat(input), + window->display->serial); frame_status_clear(frame->frame, FRAME_STATUS_MOVE); } - if ((status & FRAME_STATUS_RESIZE) && window->shell_surface) { + if ((status & FRAME_STATUS_RESIZE) && window->xdg_surface) { input_ungrab(input); - window->resizing = 1; - wl_shell_surface_resize(window->shell_surface, - input_get_seat(input), - window->display->serial, - location); + xdg_surface_resize(window->xdg_surface, + input_get_seat(input), + window->display->serial, + location); frame_status_clear(frame->frame, FRAME_STATUS_RESIZE); } } +#define DOUBLE_CLICK_PERIOD 250 static void frame_button_handler(struct widget *widget, struct input *input, uint32_t time, @@ -2431,7 +2398,27 @@ frame_button_handler(struct widget *widget, struct window_frame *frame = data; enum theme_location location; - location = frame_pointer_button(frame->frame, input, button, state); + frame->double_click = 0; + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (time - frame->last_time <= DOUBLE_CLICK_PERIOD) { + frame->double_click = 1; + frame->did_double = 1; + } else + frame->did_double = 0; + + frame->last_time = time; + } else if (frame->did_double == 1) { + frame->double_click = 1; + frame->did_double = 0; + } + + if (frame->double_click) + location = frame_double_click(frame->frame, input, + button, state); + else + location = frame_pointer_button(frame->frame, input, + button, state); + frame_handle_status(frame, input, time, location); } @@ -2461,10 +2448,17 @@ struct widget * window_frame_create(struct window *window, void *data) { struct window_frame *frame; + uint32_t buttons; + + if (window->custom) { + buttons = FRAME_BUTTON_NONE; + } else { + buttons = FRAME_BUTTON_ALL; + } frame = xzalloc(sizeof *frame); frame->frame = frame_create(window->display->theme, 0, 0, - FRAME_BUTTON_ALL, window->title); + buttons, window->title); frame->widget = window_add_widget(window, frame); frame->child = widget_add_widget(frame->widget, data); @@ -2491,9 +2485,9 @@ window_frame_set_child_size(struct widget *widget, int child_width, struct theme *t = display->theme; int decoration_width, decoration_height; int width, height; - int margin = widget->window->type == TYPE_MAXIMIZED ? 0 : t->margin; + int margin = widget->window->maximized ? 0 : t->margin; - if (widget->window->type != TYPE_FULLSCREEN) { + if (!widget->window->fullscreen) { decoration_width = (t->width + margin) * 2; decoration_height = t->width + t->titlebar_height + margin * 2; @@ -2554,10 +2548,37 @@ 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) { input->grab = widget; input->grab_button = button; + + input_set_focus_widget(input, widget, input->sx, input->sy); } void @@ -2603,16 +2624,15 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, return; } + window = wl_surface_get_user_data(surface); + if (surface != window->main_surface->surface) { + DBG("Ignoring input event from subsurface %p\n", surface); + return; + } + input->display->serial = serial; input->pointer_enter_serial = serial; - input->pointer_focus = wl_surface_get_user_data(surface); - window = input->pointer_focus; - - if (window->resizing) { - window->resizing = 0; - /* Schedule a redraw to free the pool */ - window_schedule_redraw(window); - } + input->pointer_focus = window; input->sx = sx; input->sy = sy; @@ -2642,12 +2662,12 @@ pointer_handle_motion(void *data, struct wl_pointer *pointer, float sx = wl_fixed_to_double(sx_w); float sy = wl_fixed_to_double(sy_w); - input->sx = sx; - input->sy = sy; - if (!window) return; + input->sx = sx; + input->sy = sy; + /* when making the window smaller - e.g. after a unmaximise we might * still have a pending motion event that the compositor has picked * based on the old surface dimensions @@ -2743,7 +2763,6 @@ input_remove_keyboard_focus(struct input *input) if (!window) return; - window->focus_count--; if (window->keyboard_focus_handler) (*window->keyboard_focus_handler)(window, NULL, window->user_data); @@ -2798,10 +2817,10 @@ keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, return; } - keymap = xkb_map_new_from_string(input->display->xkb_context, - map_str, - XKB_KEYMAP_FORMAT_TEXT_V1, - 0); + keymap = xkb_keymap_new_from_string(input->display->xkb_context, + map_str, + XKB_KEYMAP_FORMAT_TEXT_V1, + 0); munmap(map_str, size); close(fd); @@ -2813,7 +2832,7 @@ keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, state = xkb_state_new(keymap); if (!state) { fprintf(stderr, "failed to create XKB state\n"); - xkb_map_unref(keymap); + xkb_keymap_unref(keymap); return; } @@ -2823,11 +2842,11 @@ keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, input->xkb.state = state; input->xkb.control_mask = - 1 << xkb_map_mod_get_index(input->xkb.keymap, "Control"); + 1 << xkb_keymap_mod_get_index(input->xkb.keymap, "Control"); input->xkb.alt_mask = - 1 << xkb_map_mod_get_index(input->xkb.keymap, "Mod1"); + 1 << xkb_keymap_mod_get_index(input->xkb.keymap, "Mod1"); input->xkb.shift_mask = - 1 << xkb_map_mod_get_index(input->xkb.keymap, "Shift"); + 1 << xkb_keymap_mod_get_index(input->xkb.keymap, "Shift"); } static void @@ -2842,7 +2861,6 @@ keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, input->keyboard_focus = wl_surface_get_user_data(surface); window = input->keyboard_focus; - window->focus_count++; if (window->keyboard_focus_handler) (*window->keyboard_focus_handler)(window, input, window->user_data); @@ -2876,16 +2894,24 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, if (!window || !input->xkb.state) return; - num_syms = xkb_key_get_syms(input->xkb.state, code, &syms); + /* We only use input grabs for pointer events for now, so just + * ignore key presses if a grab is active. We expand the key + * event delivery mechanism to route events to widgets to + * properly handle key grabs. In the meantime, this prevents + * key event devlivery while a grab is active. */ + if (input->grab && input->grab_button == 0) + return; + + num_syms = xkb_state_key_get_syms(input->xkb.state, code, &syms); sym = XKB_KEY_NoSymbol; if (num_syms == 1) sym = syms[0]; + if (sym == XKB_KEY_F5 && input->modifiers == MOD_ALT_MASK) { if (state == WL_KEYBOARD_KEY_STATE_PRESSED) - window_set_maximized(window, - window->type != TYPE_MAXIMIZED); + window_set_maximized(window, !window->maximized); } else if (sym == XKB_KEY_F11 && window->fullscreen_handler && state == WL_KEYBOARD_KEY_STATE_PRESSED) { @@ -2893,11 +2919,7 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, } else if (sym == XKB_KEY_F4 && input->modifiers == MOD_ALT_MASK && state == WL_KEYBOARD_KEY_STATE_PRESSED) { - if (window->close_handler) - window->close_handler(window->parent, - window->user_data); - else - display_exit(window->display); + window_close(window); } else if (window->key_handler) { (*window->key_handler)(window, input, time, key, sym, state, window->user_data); @@ -2910,14 +2932,15 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, its.it_value.tv_sec = 0; its.it_value.tv_nsec = 0; timerfd_settime(input->repeat_timer_fd, 0, &its, NULL); - } else if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { + } else if (state == WL_KEYBOARD_KEY_STATE_PRESSED && + xkb_keymap_key_repeats(input->xkb.keymap, code)) { input->repeat_sym = sym; input->repeat_key = key; input->repeat_time = time; - its.it_interval.tv_sec = 0; - its.it_interval.tv_nsec = 25 * 1000 * 1000; - its.it_value.tv_sec = 0; - its.it_value.tv_nsec = 400 * 1000 * 1000; + its.it_interval.tv_sec = input->repeat_rate_sec; + its.it_interval.tv_nsec = input->repeat_rate_nsec; + its.it_value.tv_sec = input->repeat_delay_sec; + its.it_value.tv_nsec = input->repeat_delay_nsec; timerfd_settime(input->repeat_timer_fd, 0, &its, NULL); } } @@ -2938,8 +2961,8 @@ keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched, mods_locked, 0, 0, group); mask = xkb_state_serialize_mods(input->xkb.state, - XKB_STATE_DEPRESSED | - XKB_STATE_LATCHED); + XKB_STATE_MODS_DEPRESSED | + XKB_STATE_MODS_LATCHED); input->modifiers = 0; if (mask & input->xkb.control_mask) input->modifiers |= MOD_CONTROL_MASK; @@ -2949,12 +2972,44 @@ keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, input->modifiers |= MOD_SHIFT_MASK; } +static void +set_repeat_info(struct input *input, int32_t rate, int32_t delay) +{ + input->repeat_rate_sec = input->repeat_rate_nsec = 0; + input->repeat_delay_sec = input->repeat_delay_nsec = 0; + + /* a rate of zero disables any repeating, regardless of the delay's + * value */ + if (rate == 0) + return; + + if (rate == 1) + input->repeat_rate_sec = 1; + else + input->repeat_rate_nsec = 1000000000 / rate; + + input->repeat_delay_sec = delay / 1000; + delay -= (input->repeat_delay_sec * 1000); + input->repeat_delay_nsec = delay * 1000 * 1000; +} + +static void +keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard, + int32_t rate, int32_t delay) +{ + struct input *input = data; + + set_repeat_info(input, rate, delay); +} + static const struct wl_keyboard_listener keyboard_listener = { keyboard_handle_keymap, keyboard_handle_enter, keyboard_handle_leave, keyboard_handle_key, keyboard_handle_modifiers, + keyboard_handle_repeat_info + }; static void @@ -2974,14 +3029,25 @@ touch_handle_down(void *data, struct wl_touch *wl_touch, return; } - widget = window_find_widget(input->touch_focus, - wl_fixed_to_double(x_w), - wl_fixed_to_double(y_w)); + if (surface != input->touch_focus->main_surface->surface) { + DBG("Ignoring input event from subsurface %p\n", surface); + input->touch_focus = NULL; + return; + } + + if (input->grab) + widget = input->grab; + else + widget = window_find_widget(input->touch_focus, + wl_fixed_to_double(x_w), + wl_fixed_to_double(y_w)); if (widget) { struct touch_point *tp = xmalloc(sizeof *tp); 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) @@ -3041,6 +3107,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, @@ -3066,9 +3134,6 @@ touch_handle_frame(void *data, struct wl_touch *wl_touch) if (tp->widget->touch_frame_handler) (*tp->widget->touch_frame_handler)(tp->widget, input, tp->widget->user_data); - - wl_list_remove(&tp->link); - free(tp); } } @@ -3158,6 +3223,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) { @@ -3254,9 +3336,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); @@ -3270,7 +3357,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); @@ -3292,13 +3378,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; @@ -3314,11 +3400,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 @@ -3374,13 +3467,13 @@ input_set_pointer_image_index(struct input *input, int index) if (!buffer) return; - wl_pointer_set_cursor(input->pointer, input->pointer_enter_serial, - input->pointer_surface, - image->hotspot_x, image->hotspot_y); wl_surface_attach(input->pointer_surface, buffer, 0, 0); wl_surface_damage(input->pointer_surface, 0, 0, image->width, image->height); wl_surface_commit(input->pointer_surface); + wl_pointer_set_cursor(input->pointer, input->pointer_enter_serial, + input->pointer_surface, + image->hotspot_x, image->hotspot_y); } static const struct wl_callback_listener pointer_surface_listener; @@ -3480,14 +3573,15 @@ void input_set_selection(struct input *input, struct wl_data_source *source, uint32_t time) { - wl_data_device_set_selection(input->data_device, source, time); + if (input->data_device) + wl_data_device_set_selection(input->data_device, source, time); } 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 @@ -3535,8 +3629,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 @@ -3584,10 +3678,10 @@ input_receive_selection_data_to_fd(struct input *input, void window_move(struct window *window, struct input *input, uint32_t serial) { - if (!window->shell_surface) + if (!window->xdg_surface) return; - wl_shell_surface_move(window->shell_surface, input->seat, serial); + xdg_surface_move(window->xdg_surface, input->seat, serial); } static void @@ -3692,21 +3786,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, @@ -3727,6 +3810,49 @@ idle_resize(struct window *window) surface_set_synchronized(surface); surface_resize(surface); } + + if (!window->fullscreen && !window->maximized) + window->saved_allocation = window->pending_allocation; +} + +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 @@ -3766,52 +3892,182 @@ widget_schedule_resize(struct widget *widget, int32_t width, int32_t height) window_schedule_resize(widget->window, width, height); } +static int +window_get_shadow_margin(struct window *window) +{ + if (window->frame && !window->fullscreen) + return frame_get_shadow_margin(window->frame->frame); + else + return 0; +} + static void -handle_ping(void *data, struct wl_shell_surface *shell_surface, - uint32_t serial) +handle_surface_configure(void *data, struct xdg_surface *xdg_surface, + int32_t width, int32_t height, + struct wl_array *states, uint32_t serial) { - wl_shell_surface_pong(shell_surface, serial); + struct window *window = data; + uint32_t *p; + + window->maximized = 0; + window->fullscreen = 0; + window->resizing = 0; + window->focused = 0; + + wl_array_for_each(p, states) { + uint32_t state = *p; + switch (state) { + case XDG_SURFACE_STATE_MAXIMIZED: + window->maximized = 1; + break; + case XDG_SURFACE_STATE_FULLSCREEN: + window->fullscreen = 1; + break; + case XDG_SURFACE_STATE_RESIZING: + window->resizing = 1; + break; + case XDG_SURFACE_STATE_ACTIVATED: + window->focused = 1; + break; + default: + /* Unknown state */ + break; + } + } + + if (window->frame) { + if (window->maximized) { + frame_set_flag(window->frame->frame, FRAME_FLAG_MAXIMIZED); + } else { + frame_unset_flag(window->frame->frame, FRAME_FLAG_MAXIMIZED); + } + + if (window->focused) { + frame_set_flag(window->frame->frame, FRAME_FLAG_ACTIVE); + } else { + frame_unset_flag(window->frame->frame, FRAME_FLAG_ACTIVE); + } + } + + if (width > 0 && height > 0) { + /* The width / height params are for window geometry, + * but window_schedule_resize takes allocation. Add + * on the shadow margin to get the difference. */ + int margin = window_get_shadow_margin(window); + + window_schedule_resize(window, + width + margin * 2, + height + margin * 2); + } else { + window_schedule_resize(window, + window->saved_allocation.width, + window->saved_allocation.height); + } + + xdg_surface_ack_configure(window->xdg_surface, serial); + + if (window->state_changed_handler) + window->state_changed_handler(window, window->user_data); } static void -handle_configure(void *data, struct wl_shell_surface *shell_surface, - uint32_t edges, int32_t width, int32_t height) +handle_surface_delete(void *data, struct xdg_surface *xdg_surface) { struct window *window = data; + window_close(window); +} + +static const struct xdg_surface_listener xdg_surface_listener = { + handle_surface_configure, + handle_surface_delete, +}; + +static void +window_sync_parent(struct window *window) +{ + struct wl_surface *parent_surface; + + if (!window->xdg_surface) + return; + + if (window->parent) + parent_surface = window->parent->main_surface->surface; + else + parent_surface = NULL; + + if (parent_surface == window->last_parent_surface) + return; - window->resize_edges = edges; - window_schedule_resize(window, width, height); + xdg_surface_set_parent(window->xdg_surface, parent_surface); + window->last_parent_surface = parent_surface; } static void -menu_destroy(struct menu *menu) +window_get_geometry(struct window *window, struct rectangle *geometry) +{ + if (window->frame && !window->fullscreen) + frame_input_rect(window->frame->frame, + &geometry->x, + &geometry->y, + &geometry->width, + &geometry->height); + else + window_get_allocation(window, geometry); +} + +static void +window_sync_geometry(struct window *window) { - widget_destroy(menu->widget); - window_destroy(menu->window); - frame_destroy(menu->frame); - free(menu); + struct rectangle geometry; + + if (!window->xdg_surface) + return; + + window_get_geometry(window, &geometry); + if (geometry.x == window->last_geometry.x && + geometry.y == window->last_geometry.y && + geometry.width == window->last_geometry.width && + geometry.height == window->last_geometry.height) + return; + + xdg_surface_set_window_geometry(window->xdg_surface, + geometry.x, + geometry.y, + geometry.width, + geometry.height); + window->last_geometry = geometry; } static void -handle_popup_done(void *data, struct wl_shell_surface *shell_surface) +window_flush(struct window *window) { - struct window *window = data; - struct menu *menu = window->main_surface->widget->user_data; + struct surface *surface; - /* FIXME: Need more context in this event, at least the input - * device. Or just use wl_callback. And this really needs to - * be a window vfunc that the menu can set. And we need the - * time. */ + if (!window->custom) { + if (window->xdg_surface) { + window_sync_parent(window); + window_sync_geometry(window); + } + } - input_ungrab(menu->input); - menu_destroy(menu); + wl_list_for_each(surface, &window->subsurface_list, link) { + if (surface == window->main_surface) + continue; + + surface_flush(surface); + } + + surface_flush(window->main_surface); } -static const struct wl_shell_surface_listener shell_surface_listener = { - handle_ping, - handle_configure, - handle_popup_done -}; +static void +menu_destroy(struct menu *menu) +{ + widget_destroy(menu->widget); + window_destroy(menu->window); + frame_destroy(menu->frame); + free(menu); +} void window_get_allocation(struct window *window, @@ -3853,25 +4109,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"); @@ -3880,6 +4142,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 @@ -3887,6 +4150,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"); @@ -3901,23 +4166,40 @@ 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 window_schedule_redraw_task(struct window *window) { - if (window->configure_requests) - return; if (!window->redraw_task_scheduled) { window->redraw_task.run = idle_redraw; display_defer(window->display, &window->redraw_task); @@ -3941,111 +4223,58 @@ window_schedule_redraw(struct window *window) int window_is_fullscreen(struct window *window) { - return window->type == TYPE_FULLSCREEN; + return window->fullscreen; } -static void -configure_request_completed(void *data, struct wl_callback *callback, uint32_t time) +void +window_set_fullscreen(struct window *window, int fullscreen) { - struct window *window = data; + if (!window->xdg_surface) + return; - wl_callback_destroy(callback); - window->configure_requests--; + if (window->fullscreen == fullscreen) + return; - if (!window->configure_requests) - window_schedule_redraw(window); + if (fullscreen) + xdg_surface_set_fullscreen(window->xdg_surface, NULL); + else + xdg_surface_unset_fullscreen(window->xdg_surface); } -static struct wl_callback_listener configure_request_listener = { - configure_request_completed, -}; - -static void -window_defer_redraw_until_configure(struct window* window) +int +window_is_maximized(struct window *window) { - struct wl_callback *callback; - - if (window->redraw_task_scheduled) { - wl_list_remove(&window->redraw_task.link); - window->redraw_task_scheduled = 0; - } - - callback = wl_display_sync(window->display->display); - wl_callback_add_listener(callback, &configure_request_listener, window); - window->configure_requests++; + return window->maximized; } void -window_set_fullscreen(struct window *window, int fullscreen) +window_set_maximized(struct window *window, int maximized) { - if (!window->display->shell) + if (!window->xdg_surface) return; - if ((window->type == TYPE_FULLSCREEN) == fullscreen) + if (window->maximized == maximized) return; - if (fullscreen) { - window->saved_type = window->type; - if (window->type == TYPE_TOPLEVEL) { - window->saved_allocation = window->main_surface->allocation; - } - window->type = TYPE_FULLSCREEN; - wl_shell_surface_set_fullscreen(window->shell_surface, - window->fullscreen_method, - 0, NULL); - window_defer_redraw_until_configure (window); - } else { - if (window->saved_type == TYPE_MAXIMIZED) { - window_set_maximized(window, 1); - } else { - window->type = TYPE_TOPLEVEL; - wl_shell_surface_set_toplevel(window->shell_surface); - window_schedule_resize(window, - window->saved_allocation.width, - window->saved_allocation.height); - } - - } -} - -void -window_set_fullscreen_method(struct window *window, - enum wl_shell_surface_fullscreen_method method) -{ - window->fullscreen_method = method; + if (maximized) + xdg_surface_set_maximized(window->xdg_surface); + else + xdg_surface_unset_maximized(window->xdg_surface); } int -window_is_maximized(struct window *window) +window_is_resizing(struct window *window) { - return window->type == TYPE_MAXIMIZED; + return window->resizing; } void -window_set_maximized(struct window *window, int maximized) +window_set_minimized(struct window *window) { - if (!window->display->shell) - return; - - if ((window->type == TYPE_MAXIMIZED) == maximized) + if (!window->xdg_surface) return; - if (window->type == TYPE_TOPLEVEL) { - window->saved_allocation = window->main_surface->allocation; - wl_shell_surface_set_maximized(window->shell_surface, NULL); - window->type = TYPE_MAXIMIZED; - window_defer_redraw_until_configure(window); - } else if (window->type == TYPE_FULLSCREEN) { - wl_shell_surface_set_maximized(window->shell_surface, NULL); - window->type = TYPE_MAXIMIZED; - window_defer_redraw_until_configure(window); - } else { - wl_shell_surface_set_toplevel(window->shell_surface); - window->type = TYPE_TOPLEVEL; - window_schedule_resize(window, - window->saved_allocation.width, - window->saved_allocation.height); - } + xdg_surface_set_minimized(window->xdg_surface); } void @@ -4108,6 +4337,13 @@ window_set_output_handler(struct window *window, } void +window_set_state_changed_handler(struct window *window, + window_state_changed_handler_t handler) +{ + window->state_changed_handler = handler; +} + +void window_set_title(struct window *window, const char *title) { free(window->title); @@ -4116,8 +4352,8 @@ window_set_title(struct window *window, const char *title) 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); + if (window->xdg_surface) + xdg_surface_set_title(window->xdg_surface, title); } const char * @@ -4229,9 +4465,19 @@ surface_create(struct window *window) return surface; } +static enum window_buffer_type +get_preferred_buffer_type(struct display *display) +{ +#ifdef HAVE_CAIRO_EGL + if (display->argb_device && !getenv("TOYTOOLKIT_NO_EGL")) + return WINDOW_BUFFER_TYPE_EGL_WINDOW; +#endif + + return WINDOW_BUFFER_TYPE_SHM; +} + static struct window * -window_create_internal(struct display *display, - struct window *parent, int type) +window_create_internal(struct display *display, int custom) { struct window *window; struct surface *surface; @@ -4239,42 +4485,21 @@ 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; - if (type != TYPE_CUSTOM && display->shell) { - window->shell_surface = - wl_shell_get_shell_surface(display->shell, - surface->surface); - fail_on_null(window->shell_surface); - } + assert(custom || display->xdg_shell); - window->type = type; - window->fullscreen_method = WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT; - window->configure_requests = 0; + window->custom = custom; window->preferred_format = WINDOW_PREFERRED_FORMAT_NONE; - if (display->argb_device) -#ifdef HAVE_CAIRO_EGL - surface->buffer_type = WINDOW_BUFFER_TYPE_EGL_WINDOW; -#else - surface->buffer_type = WINDOW_BUFFER_TYPE_SHM; -#endif - else - surface->buffer_type = WINDOW_BUFFER_TYPE_SHM; + surface->buffer_type = get_preferred_buffer_type(display); wl_surface_set_user_data(surface->surface, window); wl_list_insert(display->window_list.prev, &window->link); wl_list_init(&window->redraw_task.link); - if (window->shell_surface) { - wl_shell_surface_set_user_data(window->shell_surface, window); - wl_shell_surface_add_listener(window->shell_surface, - &shell_surface_listener, window); - } - wl_list_init (&window->window_output_list); return window; @@ -4283,34 +4508,40 @@ window_create_internal(struct display *display, struct window * window_create(struct display *display) { - return window_create_internal(display, NULL, TYPE_NONE); + struct window *window; + + window = window_create_internal(display, 0); + + window->xdg_surface = + xdg_shell_get_xdg_surface(window->display->xdg_shell, + window->main_surface->surface); + fail_on_null(window->xdg_surface); + + xdg_surface_set_user_data(window->xdg_surface, window); + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); + + return window; } struct window * window_create_custom(struct display *display) { - return window_create_internal(display, NULL, TYPE_CUSTOM); + return window_create_internal(display, 1); } -struct window * -window_create_transient(struct display *display, struct window *parent, - int32_t x, int32_t y, uint32_t flags) +void +window_set_parent(struct window *window, + struct window *parent_window) { - struct window *window; - - window = window_create_internal(parent->display, - parent, TYPE_TRANSIENT); - - window->x = x; - window->y = y; - - if (display->shell) - wl_shell_surface_set_transient( - window->shell_surface, - window->parent->main_surface->surface, - window->x, window->y, flags); + window->parent = parent_window; + window_sync_parent(window); +} - return window; +struct window * +window_get_parent(struct window *window) +{ + return window->parent; } static void @@ -4374,8 +4605,7 @@ 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, input, - menu->current, menu->window->parent->user_data); + menu->func(menu->user_data, input, menu->current); input_ungrab(input); menu_destroy(menu); } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) { @@ -4384,6 +4614,20 @@ menu_button_handler(struct widget *widget, } static void +menu_touch_up_handler(struct widget *widget, + struct input *input, + uint32_t serial, + uint32_t time, + int32_t id, + void *data) +{ + struct menu *menu = data; + + input_ungrab(input); + menu_destroy(menu); +} + +static void menu_redraw_handler(struct widget *widget, void *data) { cairo_t *cr; @@ -4423,32 +4667,45 @@ menu_redraw_handler(struct widget *widget, void *data) cairo_destroy(cr); } -void -window_show_menu(struct display *display, - struct input *input, uint32_t time, struct window *parent, - int32_t x, int32_t y, - menu_func_t func, const char **entries, int count) +static void +handle_popup_popup_done(void *data, struct xdg_popup *xdg_popup, uint32_t serial) +{ + struct window *window = data; + struct menu *menu = window->main_surface->widget->user_data; + + input_ungrab(menu->input); + menu_destroy(menu); +} + +static const struct xdg_popup_listener xdg_popup_listener = { + handle_popup_popup_done, +}; + +static struct menu * +create_menu(struct display *display, + struct input *input, uint32_t time, + menu_func_t func, const char **entries, int count, + void *user_data) { struct window *window; struct menu *menu; - int32_t ix, iy; menu = malloc(sizeof *menu); if (!menu) - return; + return NULL; - window = window_create_internal(parent->display, parent, TYPE_MENU); + window = window_create_internal(display, 0); if (!window) { free(menu); - return; + return NULL; } menu->window = window; + menu->user_data = user_data; 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); + fail_on_null(menu->frame); menu->entries = entries; menu->count = count; menu->release_count = 0; @@ -4456,9 +4713,6 @@ window_show_menu(struct display *display, menu->time = time; menu->func = func; menu->input = input; - window->type = TYPE_MENU; - window->x = x; - window->y = y; input_ungrab(input); @@ -4467,6 +4721,7 @@ window_show_menu(struct display *display, widget_set_leave_handler(menu->widget, menu_leave_handler); widget_set_motion_handler(menu->widget, menu_motion_handler); widget_set_button_handler(menu->widget, menu_button_handler); + widget_set_touch_up_handler(menu->widget, menu_touch_up_handler); input_grab(input, menu->widget, 0); frame_resize_inside(menu->frame, 200, count * 20); @@ -4474,11 +4729,62 @@ window_show_menu(struct display *display, window_schedule_resize(window, frame_width(menu->frame), frame_height(menu->frame)); + return menu; +} + +struct window * +window_create_menu(struct display *display, + struct input *input, uint32_t time, + menu_func_t func, const char **entries, int count, + void *user_data) +{ + struct menu *menu; + menu = create_menu(display, input, time, func, entries, count, user_data); + + if (menu == NULL) + return NULL; + + return menu->window; +} + +void +window_show_menu(struct display *display, + struct input *input, uint32_t time, struct window *parent, + int32_t x, int32_t y, + menu_func_t func, const char **entries, int count) +{ + struct menu *menu; + struct window *window; + int32_t ix, iy; + + menu = create_menu(display, input, time, func, entries, count, parent); + + if (menu == NULL) + return; + + window = menu->window; + + window_set_buffer_scale (menu->window, window_get_buffer_scale (parent)); + window_set_buffer_transform (menu->window, window_get_buffer_transform (parent)); + + window->x = x; + window->y = y; + frame_interior(menu->frame, &ix, &iy, NULL, NULL); - wl_shell_surface_set_popup(window->shell_surface, input->seat, - display_get_serial(window->display), - window->parent->main_surface->surface, - window->x - ix, window->y - iy, 0); + + window->xdg_popup = xdg_shell_get_xdg_popup(display->xdg_shell, + window->main_surface->surface, + parent->main_surface->surface, + input->seat, + display_get_serial(window->display), + window->x - ix, + window->y - iy, + 0); + fail_on_null(window->xdg_popup); + + xdg_popup_set_user_data(window->xdg_popup, window); + xdg_popup_add_listener(window->xdg_popup, + &xdg_popup_listener, window); } void @@ -4487,6 +4793,12 @@ window_set_buffer_type(struct window *window, enum window_buffer_type type) window->main_surface->buffer_type = type; } +enum window_buffer_type +window_get_buffer_type(struct window *window) +{ + return window->main_surface->buffer_type; +} + void window_set_preferred_format(struct window *window, enum preferred_format format) @@ -4504,6 +4816,7 @@ window_add_subsurface(struct window *window, void *data, struct wl_subcompositor *subcompo = window->display->subcompositor; surface = surface_create(window); + surface->buffer_type = window_get_buffer_type(window); widget = widget_create(window, surface, data); wl_list_init(&widget->link); surface->widget = widget; @@ -4525,6 +4838,9 @@ window_add_subsurface(struct window *window, void *data, assert(!"bad enum subsurface_mode"); } + window->resize_needed = 1; + window_schedule_redraw(window); + return widget; } @@ -4544,6 +4860,14 @@ display_handle_geometry(void *data, output->allocation.x = x; output->allocation.y = y; output->transform = transform; + + if (output->make) + free(output->make); + output->make = strdup(make); + + if (output->model) + free(output->model); + output->model = strdup(model); } static void @@ -4731,11 +5055,23 @@ output_get_scale(struct output *output) return output->scale; } +const char * +output_get_make(struct output *output) +{ + return output->make; +} + +const char * +output_get_model(struct output *output) +{ + return output->model; +} + static void fini_xkb(struct input *input) { xkb_state_unref(input->xkb.state); - xkb_map_unref(input->xkb.keymap); + xkb_keymap_unref(input->xkb.keymap); } #define MIN(a,b) ((a) < (b) ? a : b) @@ -4748,7 +5084,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, - MIN(d->seat_version, 3)); + MIN(d->seat_version, 4)); input->touch_focus = NULL; input->pointer_focus = NULL; input->keyboard_focus = NULL; @@ -4758,14 +5094,19 @@ display_add_input(struct display *d, uint32_t id) wl_seat_add_listener(input->seat, &seat_listener, input); wl_seat_set_user_data(input->seat, input); - input->data_device = - wl_data_device_manager_get_data_device(d->data_device_manager, - input->seat); - wl_data_device_add_listener(input->data_device, &data_device_listener, - input); + if (d->data_device_manager) { + input->data_device = + wl_data_device_manager_get_data_device(d->data_device_manager, + input->seat); + wl_data_device_add_listener(input->data_device, + &data_device_listener, + input); + } input->pointer_surface = wl_compositor_create_surface(d->compositor); + set_repeat_info(input, 40, 400); + input->repeat_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); input->repeat_task.run = keyboard_repeat_func; @@ -4785,7 +5126,8 @@ input_destroy(struct input *input) if (input->selection_offer) data_offer_destroy(input->selection_offer); - wl_data_device_destroy(input->data_device); + if (input->data_device) + wl_data_device_destroy(input->data_device); if (input->display->seat_version >= 3) { if (input->pointer) @@ -4830,6 +5172,22 @@ struct wl_shm_listener shm_listener = { }; static void +xdg_shell_ping(void *data, struct xdg_shell *shell, uint32_t serial) +{ + xdg_shell_pong(shell, serial); +} + +static const struct xdg_shell_listener xdg_shell_listener = { + xdg_shell_ping, +}; + +#define XDG_VERSION 4 /* The version of xdg-shell that we implement */ +#ifdef static_assert +static_assert(XDG_VERSION == XDG_SHELL_VERSION_CURRENT, + "Interface version doesn't match implementation version"); +#endif + +static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { @@ -4850,9 +5208,6 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, } else if (strcmp(interface, "wl_seat") == 0) { d->seat_version = version; display_add_input(d, id); - } else if (strcmp(interface, "wl_shell") == 0) { - d->shell = wl_registry_bind(registry, - id, &wl_shell_interface, 1); } else if (strcmp(interface, "wl_shm") == 0) { d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); wl_shm_add_listener(d->shm, &shm_listener, d); @@ -4860,6 +5215,11 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, d->data_device_manager = wl_registry_bind(registry, id, &wl_data_device_manager_interface, 1); + } else if (strcmp(interface, "xdg_shell") == 0) { + d->xdg_shell = wl_registry_bind(registry, id, + &xdg_shell_interface, 1); + xdg_shell_use_unstable_version(d->xdg_shell, XDG_VERSION); + xdg_shell_add_listener(d->xdg_shell, &xdg_shell_listener, d); } else if (strcmp(interface, "text_cursor_position") == 0) { d->text_cursor_position = wl_registry_bind(registry, id, @@ -5166,8 +5526,8 @@ display_destroy(struct display *display) if (display->subcompositor) wl_subcompositor_destroy(display->subcompositor); - if (display->shell) - wl_shell_destroy(display->shell); + if (display->xdg_shell) + xdg_shell_destroy(display->xdg_shell); if (display->shm) wl_shm_destroy(display->shm); @@ -5250,7 +5610,10 @@ display_get_egl_display(struct display *d) struct wl_data_source * display_create_data_source(struct display *display) { - return wl_data_device_manager_create_data_source(display->data_device_manager); + if (display->data_device_manager) + return wl_data_device_manager_create_data_source(display->data_device_manager); + else + return NULL; } EGLConfig @@ -5259,12 +5622,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, @@ -5435,3 +5792,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)); +}