diff --git a/src/Makefile.am b/src/Makefile.am index 0b69a55..25048d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -189,6 +189,7 @@ wmaker_SOURCES += \ backend/wayland/wl_pointer_focus.c \ backend/wayland/wl_grab.c \ backend/wayland/wl_keyboard.c \ + backend/wayland/wl_popup.c \ backend/wayland/wl_xwayland.c \ backend/wayland/wl_dock.c \ backend/wayland/wl_input_fwd.c diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 3834008..7d95eed 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -295,6 +295,7 @@ void wl_update_pointer_focus(uint32_t time_msec); void wl_pointer_warp_to(int x, int y); /* wmaker-popup-v1 helper — defined in protocol section below. */ void wmaker_popup_apply(struct wl_toplevel_view *view); +extern void wmaker_popup_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id); /* Phase 14: pointer event handler forward declarations. */ extern void handle_pointer_motion(struct wl_listener *listener, void *data); /* Phase 24: click-to-focus helper — defined in Phase 13 pointer section. */ @@ -788,178 +789,7 @@ handle_new_toplevel_decoration(struct wl_listener *listener, void *data) } } -/* ------------------------------------------------------------------ */ -/* wmaker-popup-v1 protocol — client-driven popup positioning */ -/* ------------------------------------------------------------------ */ -static void -wmaker_popup_handle_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - (void)client; - wl_resource_destroy(resource); -} - -static void -wmaker_popup_handle_set_parent(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *parent_resource) -{ - (void)client; - struct wl_toplevel_view *view = wl_resource_get_user_data(resource); - if (!view) return; - if (parent_resource) { - view->popup_parent = wlr_xdg_toplevel_from_resource(parent_resource); - } else { - view->popup_parent = NULL; - } - wwarning("wmaker_popup: set_parent view=%p parent=%p", (void*)view, - (void*)view->popup_parent); -} - -static void -wmaker_popup_handle_set_position(struct wl_client *client, - struct wl_resource *resource, - int32_t x, int32_t y) -{ - (void)client; - struct wl_toplevel_view *view = wl_resource_get_user_data(resource); - wwarning("wmaker_popup: set_position view=%p (%d,%d)", (void*)view, x, y); - if (!view) return; - view->popup_x = (int)x; - view->popup_y = (int)y; - wmaker_popup_apply(view); -} - -static const struct wmaker_popup_v1_interface wmaker_popup_impl = { - .destroy = wmaker_popup_handle_destroy, - .set_parent = wmaker_popup_handle_set_parent, - .set_position = wmaker_popup_handle_set_position, -}; - -static void -wmaker_popup_resource_destroy(struct wl_resource *resource) -{ - struct wl_toplevel_view *view = wl_resource_get_user_data(resource); - if (view && view->popup == resource) - view->popup = NULL; -} - -void -wmaker_popup_apply(struct wl_toplevel_view *view) -{ - if (!view || !view->popup) return; - - /* If a parent was set, translate the requested position into screen - * coordinates by adding the parent toplevel's current position. */ - int target_x = view->popup_x; - int target_y = view->popup_y; - if (view->popup_parent) { - struct wl_toplevel_view *parent_view = - wl_find_view_by_xdg_surface(view->popup_parent->base); - if (parent_view) { - target_x += parent_view->x; - target_y += parent_view->y; - } - } - - int dx = target_x - view->x; - int dy = target_y - view->y; - wwarning("wmaker_popup_apply: view=%p (%d,%d)->(%d,%d) frame_id=0x%lx scene_node=%p", - (void*)view, view->x, view->y, target_x, target_y, - (unsigned long)view->frame_id, (void*)view->scene_node); - - view->x = target_x; - view->y = target_y; - - /* If a frame_buf wraps this view, move it via the WM's window-move - * path so the frame and its tree node update consistently. */ - if (view->frame_id) { - struct wl_frame_buf *fb = frame_buf_find(view->frame_id); - if (fb) { - fb->x += dx; - fb->y += dy; - if (fb->tree) { - wlr_scene_node_set_position(&fb->tree->node, fb->x, fb->y); - /* Raise above siblings so popups appear over their parent */ - wlr_scene_node_raise_to_top(&fb->tree->node); - wwarning("popup_apply: raised frame tree id=0x%lx to top, parent=%p", - (unsigned long)view->id, (void*)fb->tree->node.parent); - } else if (fb->scene_buf) { - wlr_scene_node_set_position(&fb->scene_buf->node, fb->x, fb->y); - wlr_scene_node_raise_to_top(&fb->scene_buf->node); - } - view->frame_rel_x = 0; - view->frame_rel_y = 0; - } - } else if (view->scene_node) { - wlr_scene_node_set_position(&view->scene_node->node, - view->x, view->y); - wlr_scene_node_raise_to_top(&view->scene_node->node); - wwarning("popup_apply: raised scene_node id=0x%lx to top", - (unsigned long)view->id); - } -} - -static void -wmaker_popup_manager_handle_destroy(struct wl_client *client, - struct wl_resource *resource) -{ - (void)client; - wl_resource_destroy(resource); -} - -static void -wmaker_popup_manager_handle_get_popup(struct wl_client *client, - struct wl_resource *manager_resource, - uint32_t id, - struct wl_resource *toplevel_resource) -{ - (void)manager_resource; - struct wlr_xdg_toplevel *xdg_top = - wlr_xdg_toplevel_from_resource(toplevel_resource); - if (!xdg_top) { - wl_resource_post_error(manager_resource, 0, - "wmaker_popup: invalid xdg_toplevel resource"); - return; - } - - struct wl_toplevel_view *view = - wl_find_view_by_xdg_surface(xdg_top->base); - - struct wl_resource *resource = wl_resource_create(client, - &wmaker_popup_v1_interface, 1, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(resource, &wmaker_popup_impl, - view, wmaker_popup_resource_destroy); - - if (view) - view->popup = resource; -} - -static const struct wmaker_popup_manager_v1_interface wmaker_popup_manager_impl = { - .destroy = wmaker_popup_manager_handle_destroy, - .get_popup = wmaker_popup_manager_handle_get_popup, -}; - -static void -wmaker_popup_manager_bind(struct wl_client *client, void *data, - uint32_t version, uint32_t id) -{ - (void)data; - struct wl_resource *resource = wl_resource_create(client, - &wmaker_popup_manager_v1_interface, - version > 1 ? 1 : (int)version, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(resource, &wmaker_popup_manager_impl, - NULL, NULL); -} /* * handle_xdg_toplevel_initial_commit -- wlroots 0.19 requires that the diff --git a/src/backend/wayland/wl_popup.c b/src/backend/wayland/wl_popup.c new file mode 100644 index 0000000..144580b --- /dev/null +++ b/src/backend/wayland/wl_popup.c @@ -0,0 +1,184 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include "wmaker-popup-v1-server-protocol.h" + +#include "wl_types.h" + +/* Forward declarations */ +struct wl_toplevel_view *wl_find_view_by_xdg_surface(struct wlr_xdg_surface *surf); +struct wl_frame_buf *frame_buf_find(WNativeWindow id); + +/* ------------------------------------------------------------------ */ +/* wmaker-popup-v1 protocol — client-driven popup positioning */ +/* ------------------------------------------------------------------ */ + +static void +wmaker_popup_handle_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + (void)client; + wl_resource_destroy(resource); +} + +static void +wmaker_popup_handle_set_parent(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *parent_resource) +{ + (void)client; + struct wl_toplevel_view *view = wl_resource_get_user_data(resource); + if (!view) return; + if (parent_resource) { + view->popup_parent = wlr_xdg_toplevel_from_resource(parent_resource); + } else { + view->popup_parent = NULL; + } + wwarning("wmaker_popup: set_parent view=%p parent=%p", (void*)view, + (void*)view->popup_parent); +} + +static void +wmaker_popup_handle_set_position(struct wl_client *client, + struct wl_resource *resource, + int32_t x, int32_t y) +{ + (void)client; + struct wl_toplevel_view *view = wl_resource_get_user_data(resource); + wwarning("wmaker_popup: set_position view=%p (%d,%d)", (void*)view, x, y); + if (!view) return; + view->popup_x = (int)x; + view->popup_y = (int)y; + wmaker_popup_apply(view); +} + +static const struct wmaker_popup_v1_interface wmaker_popup_impl = { + .destroy = wmaker_popup_handle_destroy, + .set_parent = wmaker_popup_handle_set_parent, + .set_position = wmaker_popup_handle_set_position, +}; + +static void +wmaker_popup_resource_destroy(struct wl_resource *resource) +{ + struct wl_toplevel_view *view = wl_resource_get_user_data(resource); + if (view && view->popup == resource) + view->popup = NULL; +} + +void +wmaker_popup_apply(struct wl_toplevel_view *view) +{ + if (!view || !view->popup) return; + + int target_x = view->popup_x; + int target_y = view->popup_y; + if (view->popup_parent) { + struct wl_toplevel_view *parent_view = + wl_find_view_by_xdg_surface(view->popup_parent->base); + if (parent_view) { + target_x += parent_view->x; + target_y += parent_view->y; + } + } + + int dx = target_x - view->x; + int dy = target_y - view->y; + wwarning("wmaker_popup_apply: view=%p (%d,%d)->(%d,%d) frame_id=0x%lx scene_node=%p", + (void*)view, view->x, view->y, target_x, target_y, + (unsigned long)view->frame_id, (void*)view->scene_node); + + view->x = target_x; + view->y = target_y; + + if (view->frame_id) { + struct wl_frame_buf *fb = frame_buf_find(view->frame_id); + if (fb) { + fb->x += dx; + fb->y += dy; + if (fb->tree) { + wlr_scene_node_set_position(&fb->tree->node, fb->x, fb->y); + wlr_scene_node_raise_to_top(&fb->tree->node); + wwarning("popup_apply: raised frame tree id=0x%lx to top, parent=%p", + (unsigned long)view->id, (void*)fb->tree->node.parent); + } else if (fb->scene_buf) { + wlr_scene_node_set_position(&fb->scene_buf->node, fb->x, fb->y); + wlr_scene_node_raise_to_top(&fb->scene_buf->node); + } + view->frame_rel_x = 0; + view->frame_rel_y = 0; + } + } else if (view->scene_node) { + wlr_scene_node_set_position(&view->scene_node->node, + view->x, view->y); + wlr_scene_node_raise_to_top(&view->scene_node->node); + wwarning("popup_apply: raised scene_node id=0x%lx to top", + (unsigned long)view->id); + } +} + +static void +wmaker_popup_manager_handle_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + (void)client; + wl_resource_destroy(resource); +} + +static void +wmaker_popup_manager_handle_get_popup(struct wl_client *client, + struct wl_resource *manager_resource, + uint32_t id, + struct wl_resource *toplevel_resource) +{ + (void)manager_resource; + struct wlr_xdg_toplevel *xdg_top = + wlr_xdg_toplevel_from_resource(toplevel_resource); + if (!xdg_top) { + wl_resource_post_error(manager_resource, 0, + "wmaker_popup: invalid xdg_toplevel resource"); + return; + } + + struct wl_toplevel_view *view = + wl_find_view_by_xdg_surface(xdg_top->base); + + struct wl_resource *resource = wl_resource_create(client, + &wmaker_popup_v1_interface, 1, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &wmaker_popup_impl, + view, wmaker_popup_resource_destroy); + + if (view) + view->popup = resource; +} + +static const struct wmaker_popup_manager_v1_interface wmaker_popup_manager_impl = { + .destroy = wmaker_popup_manager_handle_destroy, + .get_popup = wmaker_popup_manager_handle_get_popup, +}; + +void +wmaker_popup_manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + (void)data; + struct wl_resource *resource = wl_resource_create(client, + &wmaker_popup_manager_v1_interface, + version > 1 ? 1 : (int)version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &wmaker_popup_manager_impl, + NULL, NULL); +}