From 8b985244850b961470800d291d6de20035fea7f7 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 21 Mar 2006 14:42:25 +0000 Subject: [PATCH] Support for window groups and modal windows. git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/branches/seamlessrdp-branch/rdesktop@1176 423420c4-83ab-492f-b58f-81f9feb106b5 --- constants.h | 2 + ewmhints.c | 11 +++++- proto.h | 3 +- seamless.c | 14 ++++--- xproto.h | 1 + xwin.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 125 insertions(+), 15 deletions(-) diff --git a/constants.h b/constants.h index 41b38b9..20e54c7 100644 --- a/constants.h +++ b/constants.h @@ -419,3 +419,5 @@ enum RDP_INPUT_DEVICE #define SEAMLESSRDP_MINIMIZED 1 #define SEAMLESSRDP_MAXIMIZED 2 #define SEAMLESSRDP_POSITION_TIMER 200000 + +#define SEAMLESSRDP_CREATE_MODAL 0x0001 diff --git a/ewmhints.c b/ewmhints.c index 69315b4..ce88485 100644 --- a/ewmhints.c +++ b/ewmhints.c @@ -34,7 +34,7 @@ extern Display *g_display; static Atom g_net_wm_state_maximized_vert_atom, g_net_wm_state_maximized_horz_atom, g_net_wm_state_hidden_atom, g_net_wm_name_atom, g_utf8_string_atom, - g_net_wm_state_skip_taskbar_atom, g_net_wm_state_skip_pager_atom; + g_net_wm_state_skip_taskbar_atom, g_net_wm_state_skip_pager_atom, g_net_wm_state_modal_atom; Atom g_net_wm_state_atom, g_net_wm_desktop_atom; @@ -183,6 +183,7 @@ ewmh_init() g_net_wm_state_skip_taskbar_atom = XInternAtom(g_display, "_NET_WM_STATE_SKIP_TASKBAR", False); g_net_wm_state_skip_pager_atom = XInternAtom(g_display, "_NET_WM_STATE_SKIP_PAGER", False); + g_net_wm_state_modal_atom = XInternAtom(g_display, "_NET_WM_STATE_MODAL", False); g_net_wm_state_atom = XInternAtom(g_display, "_NET_WM_STATE", False); g_net_wm_desktop_atom = XInternAtom(g_display, "_NET_WM_DESKTOP", False); g_net_wm_name_atom = XInternAtom(g_display, "_NET_WM_NAME", False); @@ -405,6 +406,14 @@ ewmh_set_window_popup(Window wnd) return 0; } +int +ewmh_set_window_modal(Window wnd) +{ + if (ewmh_modify_state(wnd, 1, g_net_wm_state_modal_atom, 0) < 0) + return -1; + return 0; +} + #endif /* MAKE_PROTO */ diff --git a/proto.h b/proto.h index 29cc294..fe19d46 100644 --- a/proto.h +++ b/proto.h @@ -274,7 +274,8 @@ void ui_begin_update(void); void ui_end_update(void); void ui_seamless_begin(void); void ui_seamless_toggle(void); -void ui_seamless_create_window(unsigned long id, unsigned long parent, unsigned long flags); +void ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long parent, + unsigned long flags); void ui_seamless_destroy_window(unsigned long id, unsigned long flags); void ui_seamless_move_window(unsigned long id, int x, int y, int width, int height, unsigned long flags); diff --git a/seamless.c b/seamless.c index 36ffa72..90ca3f4 100644 --- a/seamless.c +++ b/seamless.c @@ -82,23 +82,27 @@ seamless_process_line(const char *line, void *data) if (!strcmp("CREATE", tok1)) { - unsigned long parent; - if (!tok5) + unsigned long group, parent; + if (!tok6) return False; id = strtoul(tok3, &endptr, 0); if (*endptr) return False; - parent = strtoul(tok4, &endptr, 0); + group = strtoul(tok4, &endptr, 0); if (*endptr) return False; - flags = strtoul(tok5, &endptr, 0); + parent = strtoul(tok5, &endptr, 0); if (*endptr) return False; - ui_seamless_create_window(id, parent, flags); + flags = strtoul(tok6, &endptr, 0); + if (*endptr) + return False; + + ui_seamless_create_window(id, group, parent, flags); } else if (!strcmp("DESTROY", tok1)) { diff --git a/xproto.h b/xproto.h index 3f6cabb..d3748a1 100644 --- a/xproto.h +++ b/xproto.h @@ -8,3 +8,4 @@ int ewmh_move_to_desktop(Window wnd, unsigned int desktop); int ewmh_get_window_desktop(Window wnd); void ewmh_set_wm_name(Window wnd, const char *title); int ewmh_set_window_popup(Window wnd); +int ewmh_set_window_modal(Window wnd); diff --git a/xwin.c b/xwin.c index f212848..fb000ab 100644 --- a/xwin.c +++ b/xwin.c @@ -28,6 +28,9 @@ #include "rdesktop.h" #include "xproto.h" +/* We can't include Xproto.h because of conflicting defines for BOOL */ +#define X_ConfigureWindow 12 + extern int g_width; extern int g_height; extern int g_xpos; @@ -50,11 +53,18 @@ static Screen *g_screen; Window g_wnd; /* SeamlessRDP support */ +typedef struct _seamless_group +{ + Window wnd; + unsigned long id; + unsigned int refcnt; +} seamless_group; typedef struct _seamless_window { Window wnd; unsigned long id; unsigned long behind; + seamless_group *group; int xoffset, yoffset; int width, height; int state; /* normal/minimized/maximized. */ @@ -304,6 +314,12 @@ sw_remove_window(seamless_window * win) if (sw == win) { *prevnext = sw->next; + sw->group->refcnt--; + if (sw->group->refcnt == 0) + { + XDestroyWindow(g_display, sw->group->wnd); + xfree(sw->group); + } xfree(sw); return; } @@ -453,6 +469,35 @@ sw_handle_restack(seamless_window * sw) } +static seamless_group * +sw_find_group(unsigned long id, BOOL dont_create) +{ + seamless_window *sw; + seamless_group *sg; + XSetWindowAttributes attribs; + + for (sw = g_seamless_windows; sw; sw = sw->next) + { + if (sw->group->id == id) + return sw->group; + } + + if (dont_create) + return NULL; + + sg = xmalloc(sizeof(seamless_group)); + + sg->wnd = + XCreateWindow(g_display, RootWindowOfScreen(g_screen), -1, -1, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, 0, &attribs); + + sg->id = id; + sg->refcnt = 0; + + return sg; +} + + static void mwm_hide_decorations(Window wnd) { @@ -1450,6 +1495,22 @@ select_visual() return True; } +static XErrorHandler g_old_error_handler; + +static int +error_handler(Display * dpy, XErrorEvent * eev) +{ + if ((eev->error_code == BadMatch) && (eev->request_code == X_ConfigureWindow)) + { + fprintf(stderr, "Got \"BadMatch\" when trying to restack windows.\n"); + fprintf(stderr, + "This is most likely caused by a broken window manager (commonly KWin).\n"); + return 0; + } + + return g_old_error_handler(dpy, eev); +} + BOOL ui_init(void) { @@ -1467,6 +1528,8 @@ ui_init(void) g_host_be = !(BOOL) (*(uint8 *) (&endianess_test)); } + g_old_error_handler = XSetErrorHandler(error_handler); + g_xserver_be = (ImageByteOrder(g_display) == MSBFirst); screen_num = DefaultScreen(g_display); g_x_socket = ConnectionNumber(g_display); @@ -3222,12 +3285,14 @@ ui_seamless_toggle() void -ui_seamless_create_window(unsigned long id, unsigned long parent, unsigned long flags) +ui_seamless_create_window(unsigned long id, unsigned long group, unsigned long parent, + unsigned long flags) { Window wnd; XSetWindowAttributes attribs; XClassHint *classhints; XSizeHints *sizehints; + XWMHints *wmhints; long input_mask; seamless_window *sw, *sw_parent; @@ -3267,10 +3332,15 @@ ui_seamless_create_window(unsigned long id, unsigned long parent, unsigned long XFree(sizehints); } - /* Handle popups without parents through some ewm hints */ + /* Parent-less transient windows */ if (parent == 0xFFFFFFFF) + { + XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen)); + /* Some buggy wm:s (kwin) do not handle the above, so fake it + using some other hints. */ ewmh_set_window_popup(wnd); - /* Set WM_TRANSIENT_FOR, if necessary */ + } + /* Normal transient windows */ else if (parent != 0x00000000) { sw_parent = sw_get_window_by_id(parent); @@ -3280,6 +3350,14 @@ ui_seamless_create_window(unsigned long id, unsigned long parent, unsigned long warning("ui_seamless_create_window: No parent window 0x%lx\n", parent); } + if (flags & SEAMLESSRDP_CREATE_MODAL) + { + /* We do this to support buggy wm:s (*cough* metacity *cough*) + somewhat at least */ + if (parent == 0x00000000) + XSetTransientForHint(g_display, wnd, RootWindowOfScreen(g_screen)); + ewmh_set_window_modal(wnd); + } /* FIXME: Support for Input Context:s */ @@ -3297,6 +3375,8 @@ ui_seamless_create_window(unsigned long id, unsigned long parent, unsigned long sw->wnd = wnd; sw->id = id; sw->behind = 0; + sw->group = sw_find_group(group, False); + sw->group->refcnt++; sw->xoffset = 0; sw->yoffset = 0; sw->width = 0; @@ -3313,6 +3393,16 @@ ui_seamless_create_window(unsigned long id, unsigned long parent, unsigned long sw->next = g_seamless_windows; g_seamless_windows = sw; + + /* WM_HINTS */ + wmhints = XAllocWMHints(); + if (wmhints) + { + wmhints->flags = WindowGroupHint; + wmhints->window_group = sw->group->wnd; + XSetWMHints(g_display, sw->wnd, wmhints); + XFree(wmhints); + } } @@ -3472,11 +3562,14 @@ ui_seamless_setstate(unsigned long id, unsigned int state, unsigned long flags) if (sw->state == SEAMLESSRDP_NOTYETMAPPED) { XWMHints *hints; - hints = XAllocWMHints(); - hints->flags = StateHint; - hints->initial_state = IconicState; - XSetWMHints(g_display, sw->wnd, hints); - XFree(hints); + hints = XGetWMHints(g_display, sw->wnd); + if (hints) + { + hints->flags |= StateHint; + hints->initial_state = IconicState; + XSetWMHints(g_display, sw->wnd, hints); + XFree(hints); + } XMapWindow(g_display, sw->wnd); } else