|
|
|
/*
|
|
|
|
* Copyright © 2011 Intel Corporation
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <X11/Xcursor/Xcursor.h>
|
|
|
|
#include <linux/input.h>
|
|
|
|
|
|
|
|
#include "compositor.h"
|
|
|
|
#include "xwayland.h"
|
|
|
|
#include "xwayland-internal-interface.h"
|
|
|
|
|
|
|
|
#include "cairo-util.h"
|
|
|
|
#include "hash.h"
|
|
|
|
#include "shared/helpers.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_DECOR_EVERYTHING \
|
|
|
|
(MWM_DECOR_BORDER | MWM_DECOR_RESIZEH | MWM_DECOR_TITLE | \
|
|
|
|
MWM_DECOR_MENU | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE)
|
|
|
|
|
|
|
|
#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;
|
|
|
|
uint32_t surface_id;
|
|
|
|
struct weston_surface *surface;
|
|
|
|
struct weston_desktop_xwayland_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;
|
|
|
|
bool pos_dirty;
|
|
|
|
int saved_width, saved_height;
|
|
|
|
int decorate;
|
|
|
|
int override_redirect;
|
|
|
|
int fullscreen;
|
|
|
|
int has_alpha;
|
|
|
|
int delete_window;
|
|
|
|
int maximized_vert;
|
|
|
|
int maximized_horz;
|
|
|
|
struct wm_size_hints size_hints;
|
|
|
|
struct motif_wm_hints motif_hints;
|
|
|
|
struct wl_list link;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct weston_wm_window *
|
|
|
|
get_wm_window(struct weston_surface *surface);
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_wm_set_net_active_window(struct weston_wm *wm, xcb_window_t window);
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_wm_window_schedule_repaint(struct weston_wm_window *window);
|
|
|
|
|
|
|
|
static void
|
|
|
|
xserver_map_shell_surface(struct weston_wm_window *window,
|
|
|
|
struct weston_surface *surface);
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool __attribute__ ((warn_unused_result))
|
|
|
|
wm_lookup_window(struct weston_wm *wm, xcb_window_t hash,
|
|
|
|
struct weston_wm_window **window)
|
|
|
|
{
|
|
|
|
*window = hash_table_lookup(wm->window_hash, hash);
|
|
|
|
if (*window)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
const struct weston_desktop_xwayland_interface *xwayland_interface =
|
|
|
|
wm->server->compositor->xwayland_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;
|
|
|
|
char name[1024];
|
|
|
|
|
|
|
|
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 ? 0 : MWM_DECOR_EVERYTHING;
|
|
|
|
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);
|
|
|
|
if (!wm_lookup_window(wm, *xid, p))
|
|
|
|
weston_log("XCB_ATOM_WINDOW contains window"
|
|
|
|
" id not found in hash table.\n");
|
|
|
|
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;
|
|
|
|
if (atom[i] == wm->atom.net_wm_state_maximized_vert)
|
|
|
|
window->maximized_vert = 1;
|
|
|
|
if (atom[i] == wm->atom.net_wm_state_maximized_horz)
|
|
|
|
window->maximized_horz = 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) {
|
|
|
|
if (window->motif_hints.decorations & MWM_DECOR_ALL)
|
|
|
|
/* MWM_DECOR_ALL means all except the other values listed. */
|
|
|
|
window->decorate =
|
|
|
|
MWM_DECOR_EVERYTHING & (~window->motif_hints.decorations);
|
|
|
|
else
|
|
|
|
window->decorate =
|
|
|
|
window->motif_hints.decorations;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
free(reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window->pid > 0) {
|
|
|
|
gethostname(name, sizeof(name));
|
|
|
|
for (i = 0; i < sizeof(name); i++) {
|
|
|
|
if (name[i] == '\0')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == sizeof(name))
|
|
|
|
name[0] = '\0'; /* ignore stupid hostnames */
|
|
|
|
|
|
|
|
/* 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 (!window->machine || strcmp(window->machine, name))
|
|
|
|
window->pid = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window->shsurf && window->name)
|
|
|
|
xwayland_interface->set_title(window->shsurf, window->name);
|
|
|
|
if (window->frame && window->name)
|
|
|
|
frame_set_title(window->frame, window->name);
|
|
|
|
if (window->shsurf && window->pid > 0)
|
|
|
|
xwayland_interface->set_pid(window->shsurf, window->pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (!wm_lookup_window(wm, configure_request->window, &window))
|
|
|
|
return;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (!wm_lookup_window(wm, configure_notify->window, &window))
|
|
|
|
return;
|
|
|
|
|
|
|
|
window->x = configure_notify->x;
|
|
|
|
window->y = configure_notify->y;
|
|
|
|
window->pos_dirty = false;
|
|
|
|
|
|
|
|
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);
|
|
|
|
if (!window)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (window->pid > 0)
|
|
|
|
kill(window->pid, SIGKILL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_wm_create_surface(struct wl_listener *listener, void *data)
|
|
|
|
{
|
|
|
|
struct weston_surface *surface = data;
|
|
|
|
struct weston_wm *wm =
|
|
|
|
container_of(listener,
|
|
|
|
struct weston_wm, create_surface_listener);
|
|
|
|
struct weston_wm_window *window;
|
|
|
|
|
|
|
|
if (wl_resource_get_client(surface->resource) != wm->server->client)
|
|
|
|
return;
|
|
|
|
|
|
|
|
wl_list_for_each(window, &wm->unpaired_window_list, link)
|
|
|
|
if (window->surface_id ==
|
|
|
|
wl_resource_get_id(surface->resource)) {
|
|
|
|
xserver_map_shell_surface(window, surface);
|
|
|
|
window->surface_id = 0;
|
|
|
|
wl_list_remove(&window->link);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_wm_send_focus_window(struct weston_wm *wm,
|
|
|
|
struct weston_wm_window *window)
|
|
|
|
{
|
|
|
|
xcb_client_message_event_t client_message;
|
|
|
|
|
|
|
|
if (window) {
|
|
|
|
uint32_t values[1];
|
|
|
|
|
|
|
|
if (window->override_redirect)
|
|
|
|
return;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
values[0] = XCB_STACK_MODE_ABOVE;
|
|
|
|
xcb_configure_window (wm->conn, window->frame_id,
|
|
|
|
XCB_CONFIG_WINDOW_STACK_MODE, values);
|
|
|
|
} else {
|
|
|
|
xcb_set_input_focus (wm->conn,
|
|
|
|
XCB_INPUT_FOCUS_POINTER_ROOT,
|
|
|
|
XCB_NONE,
|
|
|
|
XCB_TIME_CURRENT_TIME);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_wm_window_activate(struct wl_listener *listener, void *data)
|
|
|
|
{
|
|
|
|
struct weston_surface_activation_data *activation_data = data;
|
|
|
|
struct weston_surface *surface = activation_data->surface;
|
|
|
|
struct weston_wm_window *window = NULL;
|
|
|
|
struct weston_wm *wm =
|
|
|
|
container_of(listener, struct weston_wm, activate_listener);
|
|
|
|
|
|
|
|
if (surface) {
|
|
|
|
window = get_wm_window(surface);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window) {
|
|
|
|
weston_wm_set_net_active_window(wm, window->id);
|
|
|
|
} else {
|
|
|
|
weston_wm_set_net_active_window(wm, XCB_WINDOW_NONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
weston_wm_send_focus_window(wm, window);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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[3];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
if (window->fullscreen)
|
|
|
|
property[i++] = wm->atom.net_wm_state_fullscreen;
|
|
|
|
if (window->maximized_vert)
|
|
|
|
property[i++] = wm->atom.net_wm_state_maximized_vert;
|
|
|
|
if (window->maximized_horz)
|
|
|
|
property[i++] = wm->atom.net_wm_state_maximized_horz;
|
|
|
|
|
|
|
|
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;
|
|
|
|
int buttons = FRAME_BUTTON_CLOSE;
|
|
|
|
|
|
|
|
if (window->decorate & MWM_DECOR_MAXIMIZE)
|
|
|
|
buttons |= FRAME_BUTTON_MAXIMIZE;
|
|
|
|
|
|
|
|
window->frame = frame_create(window->wm->theme,
|
|
|
|
window->width, window->height,
|
|
|
|
buttons, 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets the _NET_WM_DESKTOP property for the window to 'desktop'.
|
|
|
|
* Passing a <0 desktop value deletes the property.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
weston_wm_window_set_virtual_desktop(struct weston_wm_window *window,
|
|
|
|
int desktop)
|
|
|
|
{
|
|
|
|
if (desktop >= 0) {
|
|
|
|
xcb_change_property(window->wm->conn,
|
|
|
|
XCB_PROP_MODE_REPLACE,
|
|
|
|
window->id,
|
|
|
|
window->wm->atom.net_wm_desktop,
|
|
|
|
XCB_ATOM_CARDINAL,
|
|
|
|
32, /* format */
|
|
|
|
1, &desktop);
|
|
|
|
} else {
|
|
|
|
xcb_delete_property(window->wm->conn,
|
|
|
|
window->id,
|
|
|
|
window->wm->atom.net_wm_desktop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!wm_lookup_window(wm, map_request->window, &window))
|
|
|
|
return;
|
|
|
|
|
|
|
|
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, %dx%d @ %d,%d)\n",
|
|
|
|
window->id, window, window->frame_id,
|
|
|
|
window->width, window->height, window->x, window->y);
|
|
|
|
|
|
|
|
weston_wm_window_set_wm_state(window, ICCCM_NORMAL_STATE);
|
|
|
|
weston_wm_window_set_net_wm_state(window);
|
|
|
|
weston_wm_window_set_virtual_desktop(window, 0);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (!wm_lookup_window(wm, unmap_notify->window, &window))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (window->surface_id) {
|
|
|
|
/* Make sure we're not on the unpaired surface list or we
|
|
|
|
* could be assigned a surface during surface creation that
|
|
|
|
* was mapped before this unmap request.
|
|
|
|
*/
|
|
|
|
wl_list_remove(&window->link);
|
|
|
|
window->surface_id = 0;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
|
|
|
|
weston_wm_window_set_wm_state(window, ICCCM_WITHDRAWN_STATE);
|
|
|
|
weston_wm_window_set_virtual_desktop(window, -1);
|
|
|
|
|
|
|
|
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;
|
|
|
|
const struct weston_desktop_xwayland_interface *xwayland_interface =
|
|
|
|
wm->server->compositor->xwayland_interface;
|
|
|
|
uint32_t flags = 0;
|
|
|
|
struct weston_view *view;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
render_shadow(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);
|
|
|
|
}
|
|
|
|
wl_list_for_each(view, &window->surface->views, surface_link)
|
|
|
|
weston_view_geometry_dirty(view);
|
|
|
|
|
|
|
|
pixman_region32_fini(&window->surface->pending.input);
|
|
|
|
|
|
|
|
if (window->decorate && !window->fullscreen) {
|
|
|
|
frame_input_rect(window->frame, &input_x, &input_y,
|
|
|
|
&input_w, &input_h);
|
|
|
|
} else {
|
|
|
|
input_x = x;
|
|
|
|
input_y = y;
|
|
|
|
input_w = width;
|
|
|
|
input_h = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
pixman_region32_init_rect(&window->surface->pending.input,
|
|
|
|
input_x, input_y, input_w, input_h);
|
shell: Replace set_margin with set_window_geometry
Currently, there is a fun flicker when toggling maximization or
fullscreen on a window in mutter or more sophisicated compositors
and WMs.
What happens is that the client want so go maximized, so we
calculate the size that we want the window to resize to (640x480),
and then add on its margins to find the buffer size (+10 = 660x500),
and then send out a configure event for that size. The client
renders to that size, realizes that it's maximized, and then
says "oh hey, my margins are actually 0 now!", and so the compositor
has to send out another configure event.
In order to fix this, make the the configure request correspond to
the window geometry we'd like the window to be at. At the same time,
replace set_margin with set_window_geometry, where we specify a rect
rather than a border around the window.
11 years ago
|
|
|
|
|
|
|
xwayland_interface->set_window_geometry(window->shsurf,
|
|
|
|
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;
|
|
|
|
struct weston_view *view;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
wl_list_for_each(view, &window->surface->views, surface_link)
|
|
|
|
weston_view_geometry_dirty(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;
|
|
|
|
|
|
|
|
if (!wm_lookup_window(wm, property_notify->window, &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_EVENT_MASK_FOCUS_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;
|
|
|
|
window->pos_dirty = false;
|
|
|
|
|
|
|
|
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);
|
|
|
|
weston_wm_window_set_virtual_desktop(window, -1);
|
|
|
|
hash_table_remove(wm->window_hash, window->frame_id);
|
|
|
|
window->frame_id = XCB_WINDOW_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window->surface_id)
|
|
|
|
wl_list_remove(&window->link);
|
|
|
|
|
|
|
|
if (window->surface)
|
|
|
|
wl_list_remove(&window->surface_destroy_listener.link);
|
|
|
|
|
|
|
|
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, at (%d, %d), width %d, height %d%s%s)\n",
|
|
|
|
create_notify->window,
|
|
|
|
create_notify->x, create_notify->y,
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (!wm_lookup_window(wm, destroy_notify->window, &window))
|
|
|
|
return;
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
if (!wm_lookup_window(wm, reparent_notify->window, &window))
|
|
|
|
return;
|
|
|
|
weston_wm_window_destroy(window);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct weston_seat *
|
|
|
|
weston_wm_pick_seat(struct weston_wm *wm)
|
|
|
|
{
|
|
|
|
struct wl_list *seats = wm->server->compositor->seat_list.next;
|
|
|
|
if (wl_list_empty(seats))
|
|
|
|
return NULL;
|
|
|
|
return container_of(seats, struct weston_seat, link);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct weston_seat *
|
|
|
|
weston_wm_pick_seat_for_window(struct weston_wm_window *window)
|
|
|
|
{
|
|
|
|
struct weston_wm *wm = window->wm;
|
|
|
|
struct weston_seat *seat, *s;
|
|
|
|
|
|
|
|
seat = NULL;
|
|
|
|
wl_list_for_each(s, &wm->server->compositor->seat_list, link) {
|
|
|
|
struct weston_pointer *pointer = weston_seat_get_pointer(s);
|
|
|
|
struct weston_pointer *old_pointer =
|
|
|
|
weston_seat_get_pointer(seat);
|
|
|
|
|
|
|
|
if (pointer && pointer->focus &&
|
|
|
|
pointer->focus->surface == window->surface &&
|
|
|
|
pointer->button_count > 0 &&
|
|
|
|
(!seat ||
|
|
|
|
pointer->grab_serial -
|
|
|
|
old_pointer->grab_serial < (1 << 30)))
|
|
|
|
seat = s;
|
|
|
|
}
|
|
|
|
|
|
|
|
return seat;
|
|
|
|
}
|
|
|
|
|
|
|
|
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_for_window(window);
|
|
|
|
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
|
|
|
|
int detail;
|
|
|
|
const struct weston_desktop_xwayland_interface *xwayland_interface =
|
|
|
|
wm->server->compositor->xwayland_interface;
|
|
|
|
|
|
|
|
if (!pointer || pointer->button_count != 1
|
|
|
|
|| !pointer->focus
|
|
|
|
|| pointer->focus->surface != window->surface)
|
|
|
|
return;
|
|
|
|
|
|
|
|
detail = client_message->data.data32[2];
|
|
|
|
switch (detail) {
|
|
|
|
case _NET_WM_MOVERESIZE_MOVE:
|
|
|
|
xwayland_interface->move(window->shsurf, pointer);
|
|
|
|
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:
|
|
|
|
xwayland_interface->resize(window->shsurf, pointer, 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_set_toplevel(struct weston_wm_window *window)
|
|
|
|
{
|
|
|
|
const struct weston_desktop_xwayland_interface *xwayland_interface =
|
|
|
|
window->wm->server->compositor->xwayland_interface;
|
|
|
|
|
|
|
|
xwayland_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 inline bool
|
|
|
|
weston_wm_window_is_maximized(struct weston_wm_window *window)
|
|
|
|
{
|
|
|
|
return window->maximized_horz && window->maximized_vert;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
const struct weston_desktop_xwayland_interface *xwayland_interface =
|
|
|
|
wm->server->compositor->xwayland_interface;
|
|
|
|
uint32_t action, property;
|
|
|
|
int maximized = weston_wm_window_is_maximized(window);
|
|
|
|
|
|
|
|
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)
|
|
|
|
xwayland_interface->set_fullscreen(window->shsurf,
|
|
|
|
NULL);
|
|
|
|
} else {
|
|
|
|
if (window->shsurf)
|
|
|
|
weston_wm_window_set_toplevel(window);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (property == wm->atom.net_wm_state_maximized_vert &&
|
|
|
|
update_state(action, &window->maximized_vert))
|
|
|
|
weston_wm_window_set_net_wm_state(window);
|
|
|
|
if (property == wm->atom.net_wm_state_maximized_horz &&
|
|
|
|
update_state(action, &window->maximized_horz))
|
|
|
|
weston_wm_window_set_net_wm_state(window);
|
|
|
|
|
|
|
|
if (maximized != weston_wm_window_is_maximized(window)) {
|
|
|
|
if (weston_wm_window_is_maximized(window)) {
|
|
|
|
window->saved_width = window->width;
|
|
|
|
window->saved_height = window->height;
|
|
|
|
|
|
|
|
if (window->shsurf)
|
|
|
|
xwayland_interface->set_maximized(window->shsurf);
|
|
|
|
} else if (window->shsurf) {
|
|
|
|
weston_wm_window_set_toplevel(window);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 void
|
|
|
|
weston_wm_window_handle_surface_id(struct weston_wm_window *window,
|
|
|
|
xcb_client_message_event_t *client_message)
|
|
|
|
{
|
|
|
|
struct weston_wm *wm = window->wm;
|
|
|
|
struct wl_resource *resource;
|
|
|
|
|
|
|
|
if (window->surface_id != 0) {
|
|
|
|
wm_log("already have surface id for window %d\n", window->id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Xwayland will send the wayland requests to create the
|
|
|
|
* wl_surface before sending this client message. Even so, we
|
|
|
|
* can end up handling the X event before the wayland requests
|
|
|
|
* and thus when we try to look up the surface ID, the surface
|
|
|
|
* hasn't been created yet. In that case put the window on
|
|
|
|
* the unpaired window list and continue when the surface gets
|
|
|
|
* created. */
|
|
|
|
uint32_t id = client_message->data.data32[0];
|
|
|
|
resource = wl_client_get_object(wm->server->client, id);
|
|
|
|
if (resource) {
|
|
|
|
window->surface_id = 0;
|
|
|
|
xserver_map_shell_surface(window,
|
|
|
|
wl_resource_get_user_data(resource));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
window->surface_id = id;
|
|
|
|
wl_list_insert(&wm->unpaired_window_list, &window->link);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
/* The window may get created and destroyed before we actually
|
|
|
|
* handle the message. If it doesn't exist, bail.
|
|
|
|
*/
|
|
|
|
if (!wm_lookup_window(wm, client_message->window, &window))
|
|
|
|
return;
|
|
|
|
|
|
|
|
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);
|
|
|
|
else if (client_message->type == wm->atom.wl_surface_id)
|
|
|
|
weston_wm_window_handle_surface_id(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)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
const struct weston_desktop_xwayland_interface *xwayland_interface =
|
|
|
|
wm->server->compositor->xwayland_interface;
|
|
|
|
struct weston_seat *seat;
|
|
|
|
struct weston_pointer *pointer;
|
|
|
|
struct weston_wm_window *window;
|
|
|
|
enum theme_location location;
|
|
|
|
enum wl_pointer_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);
|
|
|
|
|
|
|
|
if (!wm_lookup_window(wm, button->event, &window) ||
|
|
|
|
!window->decorate)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (button->detail != 1 && button->detail != 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
seat = weston_wm_pick_seat_for_window(window);
|
|
|
|
pointer = weston_seat_get_pointer(seat);
|
|
|
|
|
|
|
|
button_state = button->response_type == XCB_BUTTON_PRESS ?
|
|
|
|
WL_POINTER_BUTTON_STATE_PRESSED :
|
|
|
|
WL_POINTER_BUTTON_STATE_RELEASED;
|
|
|
|
button_id = button->detail == 1 ? BTN_LEFT : BTN_RIGHT;
|
|
|
|
|
|
|
|
/* Make sure we're looking at the right location. The frame
|
|
|
|
* could have received a motion event from a pointer from a
|
|
|
|
* different wl_seat, but under X it looks like our core
|
|
|
|
* pointer moved. Move the frame pointer to the button press
|
|
|
|
* location before deciding what to do. */
|
|
|
|
location = frame_pointer_motion(window->frame, NULL,
|
|
|
|
button->event_x, button->event_y);
|
|
|
|
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) {
|
|
|
|
if (pointer)
|
|
|
|
xwayland_interface->move(window->shsurf, pointer);
|
|
|
|
frame_status_clear(window->frame, FRAME_STATUS_MOVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame_status(window->frame) & FRAME_STATUS_RESIZE) {
|
|
|
|
if (pointer)
|
|
|
|
xwayland_interface->resize(window->shsurf, pointer, 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame_status(window->frame) & FRAME_STATUS_MAXIMIZE) {
|
|
|
|
window->maximized_horz = !window->maximized_horz;
|
|
|
|
window->maximized_vert = !window->maximized_vert;
|
|
|
|
if (weston_wm_window_is_maximized(window)) {
|
|
|
|
window->saved_width = window->width;
|
|
|
|
window->saved_height = window->height;
|
|
|
|
xwayland_interface->set_maximized(window->shsurf);
|
|
|
|
} else {
|
|
|
|
weston_wm_window_set_toplevel(window);
|
|
|
|
}
|
|
|
|
frame_status_clear(window->frame, FRAME_STATUS_MAXIMIZE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (!wm_lookup_window(wm, motion->event, &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;
|
|
|
|
|
|
|
|
if (!wm_lookup_window(wm, enter->event, &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;
|
|
|
|
|
|
|
|
if (!wm_lookup_window(wm, leave->event, &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 void
|
|
|
|
weston_wm_handle_focus_in(struct weston_wm *wm, xcb_generic_event_t *event)
|
|
|
|
{
|
|
|
|
xcb_focus_in_event_t *focus = (xcb_focus_in_event_t *) event;
|
|
|
|
/* Do not let X clients change the focus behind the compositor's
|
|
|
|
* back. Reset the focus to the old one if it changed. */
|
|
|
|
if (!wm->focus_window || focus->event != wm->focus_window->id)
|
|
|
|
weston_wm_send_focus_window(wm, wm->focus_window);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
case XCB_FOCUS_IN:
|
|
|
|
weston_wm_handle_focus_in(wm, event);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(event);
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count != 0)
|
|
|
|
xcb_flush(wm->conn);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_wm_set_net_active_window(struct weston_wm *wm, xcb_window_t window) {
|
|
|
|
xcb_change_property(wm->conn, XCB_PROP_MODE_REPLACE,
|
|
|
|
wm->screen->root, wm->atom.net_active_window,
|
|
|
|
wm->atom.window, 32, 1, &window);
|
|
|
|
}
|
|
|
|
|
|
|
|
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_MAXIMIZED_VERT", F(atom.net_wm_state_maximized_vert) },
|
|
|
|
{ "_NET_WM_STATE_MAXIMIZED_HORZ", F(atom.net_wm_state_maximized_horz) },
|
|
|
|
{ "_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_DESKTOP", F(atom.net_wm_desktop) },
|
|
|
|
{ "_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) },
|
|
|
|
{ "_NET_ACTIVE_WINDOW", F(atom.net_active_window) },
|
|
|
|
{ "_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) },
|
|
|
|
{ "WINDOW", F(atom.window) },
|
|
|
|
{ "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) },
|
|
|
|
{ "WL_SURFACE_ID", F(atom.wl_surface_id) }
|
|
|
|
};
|
|
|
|
#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, int fd)
|
|
|
|
{
|
|
|
|
struct weston_wm *wm;
|
|
|
|
struct wl_event_loop *loop;
|
|
|
|
xcb_screen_iterator_t s;
|
|
|
|
uint32_t values[1];
|
|
|
|
xcb_atom_t supported[6];
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* xcb_connect_to_fd takes ownership of the fd. */
|
|
|
|
wm->conn = xcb_connect_to_fd(fd, NULL);
|
|
|
|
if (xcb_connection_has_error(wm->conn)) {
|
|
|
|
weston_log("xcb_connect_to_fd failed\n");
|
|
|
|
close(fd);
|
|
|
|
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, fd,
|
|
|
|
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();
|
|
|
|
|
|
|
|
supported[0] = wm->atom.net_wm_moveresize;
|
|
|
|
supported[1] = wm->atom.net_wm_state;
|
|
|
|
supported[2] = wm->atom.net_wm_state_fullscreen;
|
|
|
|
supported[3] = wm->atom.net_wm_state_maximized_vert;
|
|
|
|
supported[4] = wm->atom.net_wm_state_maximized_horz;
|
|
|
|
supported[5] = wm->atom.net_active_window;
|
|
|
|
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_set_net_active_window(wm, XCB_WINDOW_NONE);
|
|
|
|
|
|
|
|
weston_wm_selection_init(wm);
|
|
|
|
|
|
|
|
weston_wm_dnd_init(wm);
|
|
|
|
|
|
|
|
xcb_flush(wm->conn);
|
|
|
|
|
|
|
|
wm->create_surface_listener.notify = weston_wm_create_surface;
|
|
|
|
wl_signal_add(&wxs->compositor->create_surface_signal,
|
|
|
|
&wm->create_surface_listener);
|
|
|
|
wm->activate_listener.notify = weston_wm_window_activate;
|
|
|
|
wl_signal_add(&wxs->compositor->activate_signal,
|
|
|
|
&wm->activate_listener);
|
|
|
|
wm->kill_listener.notify = weston_wm_kill_client;
|
|
|
|
wl_signal_add(&wxs->compositor->kill_signal,
|
|
|
|
&wm->kill_listener);
|
|
|
|
wl_list_init(&wm->unpaired_window_list);
|
|
|
|
|
|
|
|
weston_wm_create_cursors(wm);
|
|
|
|
weston_wm_window_set_cursor(wm, wm->screen->root, XWM_CURSOR_LEFT_PTR);
|
|
|
|
|
|
|
|
/* Create wm window and take WM_S0 selection last, which
|
|
|
|
* signals to Xwayland that we're done with setup. */
|
|
|
|
weston_wm_create_wm_window(wm);
|
|
|
|
|
|
|
|
weston_log("created wm, root %d\n", wm->screen->root);
|
|
|
|
|
|
|
|
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->create_surface_listener.link);
|
|
|
|
|
|
|
|
free(wm);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 bool
|
|
|
|
is_wm_window(struct weston_surface *surface)
|
|
|
|
{
|
|
|
|
return get_wm_window(surface) != 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, 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->decorate && !window->fullscreen) {
|
|
|
|
hborder = 2 * t->width;
|
|
|
|
vborder = t->titlebar_height + t->width;
|
|
|
|
} else {
|
|
|
|
hborder = 0;
|
|
|
|
vborder = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 void
|
|
|
|
send_position(struct weston_surface *surface, int32_t x, int32_t y)
|
|
|
|
{
|
|
|
|
struct weston_wm_window *window = get_wm_window(surface);
|
|
|
|
struct weston_wm *wm;
|
|
|
|
uint32_t mask, values[2];
|
|
|
|
|
|
|
|
if (!window || !window->wm)
|
|
|
|
return;
|
|
|
|
|
|
|
|
wm = window->wm;
|
|
|
|
/* We use pos_dirty to tell whether a configure message is in flight.
|
|
|
|
* This is needed in case we send two configure events in a very
|
|
|
|
* short time, since window->x/y is set in after a roundtrip, hence
|
|
|
|
* we cannot just check if the current x and y are different. */
|
|
|
|
if (window->x != x || window->y != y || window->pos_dirty) {
|
|
|
|
window->pos_dirty = true;
|
|
|
|
values[0] = x;
|
|
|
|
values[1] = y;
|
|
|
|
mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
|
|
|
|
|
|
|
|
xcb_configure_window(wm->conn, window->frame_id, mask, values);
|
|
|
|
xcb_flush(wm->conn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct weston_xwayland_client_interface 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 bool
|
|
|
|
weston_wm_window_type_inactive(struct weston_wm_window *window)
|
|
|
|
{
|
|
|
|
struct weston_wm *wm = window->wm;
|
|
|
|
|
|
|
|
return window->type == wm->atom.net_wm_window_type_tooltip ||
|
|
|
|
window->type == wm->atom.net_wm_window_type_dropdown ||
|
|
|
|
window->type == wm->atom.net_wm_window_type_dnd ||
|
|
|
|
window->type == wm->atom.net_wm_window_type_combo ||
|
|
|
|
window->type == wm->atom.net_wm_window_type_popup ||
|
|
|
|
window->type == wm->atom.net_wm_window_type_utility;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xserver_map_shell_surface(struct weston_wm_window *window,
|
|
|
|
struct weston_surface *surface)
|
|
|
|
{
|
|
|
|
struct weston_wm *wm = window->wm;
|
|
|
|
struct weston_desktop_xwayland *xwayland =
|
|
|
|
wm->server->compositor->xwayland;
|
|
|
|
const struct weston_desktop_xwayland_interface *xwayland_interface =
|
|
|
|
wm->server->compositor->xwayland_interface;
|
|
|
|
struct weston_output *output;
|
|
|
|
struct weston_wm_window *parent;
|
|
|
|
|
|
|
|
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 = surface;
|
|
|
|
window->surface_destroy_listener.notify = surface_destroy;
|
|
|
|
wl_signal_add(&window->surface->destroy_signal,
|
|
|
|
&window->surface_destroy_listener);
|
|
|
|
|
|
|
|
weston_wm_window_schedule_repaint(window);
|
|
|
|
|
|
|
|
if (!xwayland_interface)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (window->surface->committed) {
|
compositor: send error for surface role resets
With the more accurate definition of wl_surface roles in Wayland,
enforce the restriction: a role is always set permanently, and
attempting to change it is a protocol error.
This patch is based on Jasper's patch:
http://lists.freedesktop.org/archives/wayland-devel/2014-August/016811.html
The difference in this patch compared to his are:
- send role errors on the interface whose request triggers it, not on
wl_surface
- an interface could have several requests assigning different roles,
cannot use wl_interface as the unique key; use an arbitary string
instead
- ensure in window-manager.c that create_shell_surface() ->
create_common_surface() is never called with surface->configure set,
to avoid compositor abort
- use wl_resource_post_no_memory() where appropriate instead of
hand-rolling it with wl_resource_post_error()
Ideally we would not add weston_surface::role_name field, but use
weston_surface::configure. At the moment this is not possible though,
because at least shell.c uses several different roles with the same
configure function. Drag'n'drop uses two configure functions for the
same role. The configure hook is also reset in several places,
which is not good for role tracking.
This patch overlooks the wl_surface roles assigned in privileged
extensions: screensaver, panel, background, lock, input panel.
Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Reviewed-by: Jasper St. Pierre <jstpierre@mecheye.net>
10 years ago
|
|
|
weston_log("warning, unexpected in %s: "
|
|
|
|
"surface's configure hook is already set.\n",
|
|
|
|
__func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
window->shsurf =
|
|
|
|
xwayland_interface->create_surface(xwayland,
|
|
|
|
window->surface,
|
|
|
|
&shell_client);
|
|
|
|
|
|
|
|
if (window->name)
|
|
|
|
xwayland_interface->set_title(window->shsurf, window->name);
|
|
|
|
if (window->pid > 0)
|
|
|
|
xwayland_interface->set_pid(window->shsurf, window->pid);
|
|
|
|
|
|
|
|
if (window->fullscreen) {
|
|
|
|
window->saved_width = window->width;
|
|
|
|
window->saved_height = window->height;
|
|
|
|
xwayland_interface->set_fullscreen(window->shsurf, NULL);
|
|
|
|
return;
|
|
|
|
} else if (legacy_fullscreen(wm, window, &output)) {
|
|
|
|
window->fullscreen = 1;
|
|
|
|
xwayland_interface->set_fullscreen(window->shsurf, output);
|
|
|
|
} else if (window->override_redirect) {
|
|
|
|
xwayland_interface->set_xwayland(window->shsurf,
|
|
|
|
window->x, window->y);
|
|
|
|
} else if (window->transient_for && window->transient_for->surface) {
|
|
|
|
parent = window->transient_for;
|
|
|
|
if (weston_wm_window_type_inactive(window)) {
|
|
|
|
xwayland_interface->set_transient(window->shsurf,
|
|
|
|
parent->surface,
|
|
|
|
window->x - parent->x,
|
|
|
|
window->y - parent->y);
|
|
|
|
} else {
|
|
|
|
xwayland_interface->set_toplevel(window->shsurf);
|
|
|
|
xwayland_interface->set_parent(window->shsurf,
|
|
|
|
parent->surface);
|
|
|
|
}
|
|
|
|
} else if (weston_wm_window_is_maximized(window)) {
|
|
|
|
xwayland_interface->set_maximized(window->shsurf);
|
|
|
|
} else {
|
|
|
|
if (weston_wm_window_type_inactive(window)) {
|
|
|
|
xwayland_interface->set_xwayland(window->shsurf,
|
|
|
|
window->x,
|
|
|
|
window->y);
|
|
|
|
} else {
|
|
|
|
xwayland_interface->set_toplevel(window->shsurf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct weston_xwayland_surface_api surface_api = {
|
|
|
|
is_wm_window,
|
|
|
|
send_position,
|
|
|
|
};
|