Index: tigervnc/unix/vncviewer/ewmhints.c =================================================================== --- tigervnc/unix/vncviewer/ewmhints.c (revision 0) +++ tigervnc/unix/vncviewer/ewmhints.c (revision 0) @@ -0,0 +1,1244 @@ +/* -*- c-basic-offset: 8 -*- + rdesktop: A Remote Desktop Protocol client. + + Support functions for Extended Window Manager Hints, + http://www.freedesktop.org/wiki/Standards_2fwm_2dspec + + Copyright 2005 Peter Astrand for Cendio AB + Copyright 2007 Pierre Ossman for Cendio AB + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include + +#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ +#define _NET_WM_STATE_ADD 1 /* add/set property */ +#define _NET_WM_STATE_TOGGLE 2 /* toggle property */ + +typedef unsigned int uint32; + +// FIXME: Remove, this file shouldn't depend on seamlessrdp! +#define SEAMLESSRDP_NORMAL 0 +#define SEAMLESSRDP_MINIMIZED 1 +#define SEAMLESSRDP_MAXIMIZED 2 + +#ifndef EX_UNAVAILABLE +#define EX_UNAVAILABLE 69 +#endif + +// FIXME: Need to think about this one... +/* malloc; exit if out of memory */ +static void * +xmalloc(int size) +{ + void *mem = malloc(size); + if (mem == NULL) + { + fprintf(stderr, "ERROR: xmalloc %d\n", size); + exit(EX_UNAVAILABLE); + } + return mem; +} + + +static 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_modal_atom, g_net_wm_icon_atom, g_net_wm_state_above_atom, + g_net_wm_state_fullscreen_atom; + +Atom g_net_wm_state_atom, g_net_wm_desktop_atom; + +/* + Get window property value (32 bit format) + Returns zero on success, -1 on error +*/ +static int +get_property_value(Window wnd, char *propname, long max_length, + unsigned long *nitems_return, unsigned char **prop_return, int nowarn) +{ + int result; + Atom property; + Atom actual_type_return; + int actual_format_return; + unsigned long bytes_after_return; + + property = XInternAtom(g_display, propname, True); + if (property == None) + { + fprintf(stderr, "Atom %s does not exist\n", propname); + return (-1); + } + + result = XGetWindowProperty(g_display, wnd, property, 0, /* long_offset */ + max_length, /* long_length */ + False, /* delete */ + AnyPropertyType, /* req_type */ + &actual_type_return, + &actual_format_return, + nitems_return, &bytes_after_return, prop_return); + + if (result != Success) + { + fprintf(stderr, "XGetWindowProperty failed\n"); + return (-1); + } + + if (actual_type_return == None || actual_format_return == 0) + { + if (!nowarn) + fprintf(stderr, "Window is missing property %s\n", propname); + return (-1); + } + + if (bytes_after_return) + { + fprintf(stderr, "%s is too big for me\n", propname); + return (-1); + } + + if (actual_format_return != 32) + { + fprintf(stderr, "%s has bad format\n", propname); + return (-1); + } + + return (0); +} + +/* + Get current desktop number + Returns -1 on error +*/ +static int +get_current_desktop(void) +{ + unsigned long nitems_return; + unsigned char *prop_return; + int current_desktop; + + if (get_property_value + (DefaultRootWindow(g_display), "_NET_CURRENT_DESKTOP", 1, &nitems_return, + &prop_return, 0) < 0) + return (-1); + + if (nitems_return != 1) + { + fprintf(stderr, "_NET_CURRENT_DESKTOP has bad length\n"); + return (-1); + } + + current_desktop = *prop_return; + XFree(prop_return); + return current_desktop; +} + +/* + Get workarea geometry + Returns zero on success, -1 on error + */ + +int +get_current_workarea(uint32 * x, uint32 * y, uint32 * width, uint32 * height) +{ + int current_desktop; + unsigned long nitems_return; + unsigned char *prop_return; + long *return_words; + const uint32 net_workarea_x_offset = 0; + const uint32 net_workarea_y_offset = 1; + const uint32 net_workarea_width_offset = 2; + const uint32 net_workarea_height_offset = 3; + const uint32 max_prop_length = 32 * 4; /* Max 32 desktops */ + + if (get_property_value + (DefaultRootWindow(g_display), "_NET_WORKAREA", max_prop_length, &nitems_return, + &prop_return, 0) < 0) + return (-1); + + if (nitems_return % 4) + { + fprintf(stderr, "_NET_WORKAREA has odd length\n"); + return (-1); + } + + current_desktop = get_current_desktop(); + + if (current_desktop < 0) + return -1; + + return_words = (long *) prop_return; + + *x = return_words[current_desktop * 4 + net_workarea_x_offset]; + *y = return_words[current_desktop * 4 + net_workarea_y_offset]; + *width = return_words[current_desktop * 4 + net_workarea_width_offset]; + *height = return_words[current_desktop * 4 + net_workarea_height_offset]; + + XFree(prop_return); + + return (0); + +} + + + +void +ewmh_init(Display *display) +{ + g_display = display; + /* FIXME: Use XInternAtoms */ + g_net_wm_state_maximized_vert_atom = + XInternAtom(g_display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + g_net_wm_state_maximized_horz_atom = + XInternAtom(g_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + g_net_wm_state_hidden_atom = XInternAtom(g_display, "_NET_WM_STATE_HIDDEN", False); + 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_above_atom = XInternAtom(g_display, "_NET_WM_STATE_ABOVE", False); + g_net_wm_state_atom = XInternAtom(g_display, "_NET_WM_STATE", False); + g_net_wm_state_fullscreen_atom = XInternAtom(g_display, "_NET_WM_STATE_FULLSCREEN", 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); + g_net_wm_icon_atom = XInternAtom(g_display, "_NET_WM_ICON", False); + g_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False); +} + + +/* + Get the window state: normal/minimized/maximized. +*/ +#ifndef MAKE_PROTO +int +ewmh_get_window_state(Window w) +{ + unsigned long nitems_return; + unsigned char *prop_return; + uint32 *return_words; + unsigned long item; + Bool maximized_vert, maximized_horz, hidden; + + maximized_vert = maximized_horz = hidden = False; + + if (get_property_value(w, "_NET_WM_STATE", 64, &nitems_return, &prop_return, 0) < 0) + return SEAMLESSRDP_NORMAL; + + return_words = (uint32 *) prop_return; + + for (item = 0; item < nitems_return; item++) + { + if (return_words[item] == g_net_wm_state_maximized_vert_atom) + maximized_vert = True; + if (return_words[item] == g_net_wm_state_maximized_horz_atom) + maximized_horz = True; + if (return_words[item] == g_net_wm_state_hidden_atom) + hidden = True; + } + + XFree(prop_return); + + if (maximized_vert && maximized_horz) + return SEAMLESSRDP_MAXIMIZED; + else if (hidden) + return SEAMLESSRDP_MINIMIZED; + else + return SEAMLESSRDP_NORMAL; +} + +static int +ewmh_modify_state(Window wnd, Bool add, Atom atom1, Atom atom2) +{ + Status status; + XEvent xevent; + + int result; + unsigned long nitems; + unsigned char *props; + uint32 state = WithdrawnState; + + /* The spec states that the window manager must respect any + _NET_WM_STATE attributes on a withdrawn window. In order words, we + modify the attributes directly for withdrawn windows and ask the WM + to do it for active windows. */ + result = get_property_value(wnd, "WM_STATE", 64, &nitems, &props, 1); + if ((result >= 0) && nitems) + { + state = *(uint32 *) props; + XFree(props); + } + + if (state == WithdrawnState) + { + if (add) + { + Atom atoms[2]; + + atoms[0] = atom1; + nitems = 1; + if (atom2) + { + atoms[1] = atom2; + nitems = 2; + } + + XChangeProperty(g_display, wnd, g_net_wm_state_atom, XA_ATOM, + 32, PropModeAppend, (unsigned char *) atoms, nitems); + } + else + { + Atom *atoms; + int i; + + if (get_property_value(wnd, "_NET_WM_STATE", 64, &nitems, &props, 1) < 0) + return 0; + + atoms = (Atom *) props; + + for (i = 0; i < nitems; i++) + { + if ((atoms[i] == atom1) || (atom2 && (atoms[i] == atom2))) + { + if (i != (nitems - 1)) + memmove(&atoms[i], &atoms[i + 1], + sizeof(Atom) * (nitems - i - 1)); + nitems--; + i--; + } + } + + XChangeProperty(g_display, wnd, g_net_wm_state_atom, XA_ATOM, + 32, PropModeReplace, (unsigned char *) atoms, nitems); + + XFree(props); + } + + return 0; + } + + xevent.type = ClientMessage; + xevent.xclient.window = wnd; + xevent.xclient.message_type = g_net_wm_state_atom; + xevent.xclient.format = 32; + if (add) + xevent.xclient.data.l[0] = _NET_WM_STATE_ADD; + else + xevent.xclient.data.l[0] = _NET_WM_STATE_REMOVE; + xevent.xclient.data.l[1] = atom1; + xevent.xclient.data.l[2] = atom2; + xevent.xclient.data.l[3] = 0; + xevent.xclient.data.l[4] = 0; + status = XSendEvent(g_display, DefaultRootWindow(g_display), False, + SubstructureNotifyMask | SubstructureRedirectMask, &xevent); + if (!status) + return -1; + + return 0; +} + + +int ewmh_state_fullscreen(Window wnd, Bool fullscreen) +{ + if (ewmh_modify_state(wnd, fullscreen, g_net_wm_state_fullscreen_atom, 0) < 0) + return -1; + return 0; +} + +/* + Set the window state: normal/minimized/maximized. + Returns -1 on failure. +*/ +int +ewmh_change_state(Window wnd, int state) +{ + /* + * Deal with the max atoms + */ + if (state == SEAMLESSRDP_MAXIMIZED) + { + if (ewmh_modify_state + (wnd, 1, g_net_wm_state_maximized_vert_atom, + g_net_wm_state_maximized_horz_atom) < 0) + return -1; + } + else + { + if (ewmh_modify_state + (wnd, 0, g_net_wm_state_maximized_vert_atom, + g_net_wm_state_maximized_horz_atom) < 0) + return -1; + } + + return 0; +} + + +int +ewmh_get_window_desktop(Window wnd) +{ + unsigned long nitems_return; + unsigned char *prop_return; + int desktop; + + if (get_property_value(wnd, "_NET_WM_DESKTOP", 1, &nitems_return, &prop_return, 0) < 0) + return (-1); + + if (nitems_return != 1) + { + fprintf(stderr, "_NET_WM_DESKTOP has bad length\n"); + return (-1); + } + + desktop = *prop_return; + XFree(prop_return); + return desktop; +} + + +int +ewmh_move_to_desktop(Window wnd, unsigned int desktop) +{ + Status status; + XEvent xevent; + + xevent.type = ClientMessage; + xevent.xclient.window = wnd; + xevent.xclient.message_type = g_net_wm_desktop_atom; + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = desktop; + xevent.xclient.data.l[1] = 0; + xevent.xclient.data.l[2] = 0; + xevent.xclient.data.l[3] = 0; + xevent.xclient.data.l[4] = 0; + status = XSendEvent(g_display, DefaultRootWindow(g_display), False, + SubstructureNotifyMask | SubstructureRedirectMask, &xevent); + if (!status) + return -1; + + return 0; +} + +void +ewmh_set_wm_name(Window wnd, const char *title) +{ + int len; + + len = strlen(title); + XChangeProperty(g_display, wnd, g_net_wm_name_atom, g_utf8_string_atom, + 8, PropModeReplace, (unsigned char *) title, len); +} + + +int +ewmh_set_window_popup(Window wnd) +{ + if (ewmh_modify_state + (wnd, 1, g_net_wm_state_skip_taskbar_atom, g_net_wm_state_skip_pager_atom) < 0) + return -1; + 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; +} + +void +ewmh_set_icon(Window wnd, int width, int height, const char *rgba_data) +{ + unsigned long nitems, i; + unsigned char *props; + unsigned long *cur_set, *new_set; + unsigned long *icon; + + cur_set = NULL; + new_set = NULL; + + if (get_property_value(wnd, "_NET_WM_ICON", 10000, &nitems, &props, 1) >= 0) + { + cur_set = (unsigned long *) props; + + for (i = 0; i < nitems;) + { + if (cur_set[i] == width && cur_set[i + 1] == height) + break; + + i += 2 + cur_set[i] * cur_set[i + 1]; + } + + if (i != nitems) + icon = cur_set + i; + else + { + new_set = xmalloc((nitems + width * height + 2) * sizeof(unsigned long)); + memcpy(new_set, cur_set, nitems * sizeof(unsigned long)); + icon = new_set + nitems; + nitems += width * height + 2; + } + } + else + { + new_set = xmalloc((width * height + 2) * sizeof(unsigned long)); + icon = new_set; + nitems = width * height + 2; + } + + icon[0] = width; + icon[1] = height; + + /* Convert RGBA -> ARGB */ + for (i = 0; i < width * height; i++) + { + icon[i + 2] = + rgba_data[i * 4 + 3] << 24 | + ((rgba_data[i * 4 + 0] << 16) & 0x00FF0000) | + ((rgba_data[i * 4 + 1] << 8) & 0x0000FF00) | + ((rgba_data[i * 4 + 2] << 0) & 0x000000FF); + } + + XChangeProperty(g_display, wnd, g_net_wm_icon_atom, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) (new_set ? new_set : cur_set), nitems); + + if (cur_set) + XFree(cur_set); + if (new_set) + free(new_set); +} + +void +ewmh_del_icon(Window wnd, int width, int height) +{ + unsigned long nitems, i, icon_size; + unsigned char *props; + unsigned long *cur_set, *new_set; + + cur_set = NULL; + new_set = NULL; + + if (get_property_value(wnd, "_NET_WM_ICON", 10000, &nitems, &props, 1) < 0) + return; + + cur_set = (unsigned long *) props; + + for (i = 0; i < nitems;) + { + if (cur_set[i] == width && cur_set[i + 1] == height) + break; + + i += 2 + cur_set[i] * cur_set[i + 1]; + } + + if (i == nitems) + goto out; + + icon_size = width * height + 2; + new_set = xmalloc((nitems - icon_size) * sizeof(unsigned long)); + + if (i != 0) + memcpy(new_set, cur_set, i * sizeof(unsigned long)); + if (i != nitems - icon_size) + memcpy(new_set + i, cur_set + i + icon_size, + (nitems - (i + icon_size)) * sizeof(unsigned long)); + + nitems -= icon_size; + + XChangeProperty(g_display, wnd, g_net_wm_icon_atom, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) new_set, nitems); + + free(new_set); + + out: + XFree(cur_set); +} + +int +ewmh_set_window_above(Window wnd) +{ + if (ewmh_modify_state(wnd, 1, g_net_wm_state_above_atom, 0) < 0) + return -1; + return 0; +} + +#endif /* MAKE_PROTO */ + + +#if 0 + +/* FIXME: _NET_MOVERESIZE_WINDOW is for pagers, not for + applications. We should implement _NET_WM_MOVERESIZE instead */ + +int +ewmh_net_moveresize_window(Window wnd, int x, int y, int width, int height) +{ + Status status; + XEvent xevent; + Atom moveresize; + + moveresize = XInternAtom(g_display, "_NET_MOVERESIZE_WINDOW", False); + if (!moveresize) + { + return -1; + } + + xevent.type = ClientMessage; + xevent.xclient.window = wnd; + xevent.xclient.message_type = moveresize; + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = StaticGravity | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11); + xevent.xclient.data.l[1] = x; + xevent.xclient.data.l[2] = y; + xevent.xclient.data.l[3] = width; + xevent.xclient.data.l[4] = height; + + status = XSendEvent(g_display, DefaultRootWindow(g_display), False, + SubstructureNotifyMask | SubstructureRedirectMask, &xevent); + if (!status) + return -1; + return 0; +} + +#endif +/* -*- c-basic-offset: 8 -*- + rdesktop: A Remote Desktop Protocol client. + + Support functions for Extended Window Manager Hints, + http://www.freedesktop.org/wiki/Standards_2fwm_2dspec + + Copyright 2005 Peter Astrand for Cendio AB + Copyright 2007 Pierre Ossman for Cendio AB + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include + +#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ +#define _NET_WM_STATE_ADD 1 /* add/set property */ +#define _NET_WM_STATE_TOGGLE 2 /* toggle property */ + +typedef unsigned int uint32; + +// FIXME: Remove, this file shouldn't depend on seamlessrdp! +#define SEAMLESSRDP_NORMAL 0 +#define SEAMLESSRDP_MINIMIZED 1 +#define SEAMLESSRDP_MAXIMIZED 2 + +#ifndef EX_UNAVAILABLE +#define EX_UNAVAILABLE 69 +#endif + +// FIXME: Need to think about this one... +/* malloc; exit if out of memory */ +static void * +xmalloc(int size) +{ + void *mem = malloc(size); + if (mem == NULL) + { + fprintf(stderr, "ERROR: xmalloc %d\n", size); + exit(EX_UNAVAILABLE); + } + return mem; +} + + +static 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_modal_atom, g_net_wm_icon_atom, g_net_wm_state_above_atom, + g_net_wm_state_fullscreen_atom; + +Atom g_net_wm_state_atom, g_net_wm_desktop_atom; + +/* + Get window property value (32 bit format) + Returns zero on success, -1 on error +*/ +static int +get_property_value(Window wnd, char *propname, long max_length, + unsigned long *nitems_return, unsigned char **prop_return, int nowarn) +{ + int result; + Atom property; + Atom actual_type_return; + int actual_format_return; + unsigned long bytes_after_return; + + property = XInternAtom(g_display, propname, True); + if (property == None) + { + fprintf(stderr, "Atom %s does not exist\n", propname); + return (-1); + } + + result = XGetWindowProperty(g_display, wnd, property, 0, /* long_offset */ + max_length, /* long_length */ + False, /* delete */ + AnyPropertyType, /* req_type */ + &actual_type_return, + &actual_format_return, + nitems_return, &bytes_after_return, prop_return); + + if (result != Success) + { + fprintf(stderr, "XGetWindowProperty failed\n"); + return (-1); + } + + if (actual_type_return == None || actual_format_return == 0) + { + if (!nowarn) + fprintf(stderr, "Window is missing property %s\n", propname); + return (-1); + } + + if (bytes_after_return) + { + fprintf(stderr, "%s is too big for me\n", propname); + return (-1); + } + + if (actual_format_return != 32) + { + fprintf(stderr, "%s has bad format\n", propname); + return (-1); + } + + return (0); +} + +/* + Get current desktop number + Returns -1 on error +*/ +static int +get_current_desktop(void) +{ + unsigned long nitems_return; + unsigned char *prop_return; + int current_desktop; + + if (get_property_value + (DefaultRootWindow(g_display), "_NET_CURRENT_DESKTOP", 1, &nitems_return, + &prop_return, 0) < 0) + return (-1); + + if (nitems_return != 1) + { + fprintf(stderr, "_NET_CURRENT_DESKTOP has bad length\n"); + return (-1); + } + + current_desktop = *prop_return; + XFree(prop_return); + return current_desktop; +} + +/* + Get workarea geometry + Returns zero on success, -1 on error + */ + +int +get_current_workarea(uint32 * x, uint32 * y, uint32 * width, uint32 * height) +{ + int current_desktop; + unsigned long nitems_return; + unsigned char *prop_return; + long *return_words; + const uint32 net_workarea_x_offset = 0; + const uint32 net_workarea_y_offset = 1; + const uint32 net_workarea_width_offset = 2; + const uint32 net_workarea_height_offset = 3; + const uint32 max_prop_length = 32 * 4; /* Max 32 desktops */ + + if (get_property_value + (DefaultRootWindow(g_display), "_NET_WORKAREA", max_prop_length, &nitems_return, + &prop_return, 0) < 0) + return (-1); + + if (nitems_return % 4) + { + fprintf(stderr, "_NET_WORKAREA has odd length\n"); + return (-1); + } + + current_desktop = get_current_desktop(); + + if (current_desktop < 0) + return -1; + + return_words = (long *) prop_return; + + *x = return_words[current_desktop * 4 + net_workarea_x_offset]; + *y = return_words[current_desktop * 4 + net_workarea_y_offset]; + *width = return_words[current_desktop * 4 + net_workarea_width_offset]; + *height = return_words[current_desktop * 4 + net_workarea_height_offset]; + + XFree(prop_return); + + return (0); + +} + + + +void +ewmh_init(Display *display) +{ + g_display = display; + /* FIXME: Use XInternAtoms */ + g_net_wm_state_maximized_vert_atom = + XInternAtom(g_display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + g_net_wm_state_maximized_horz_atom = + XInternAtom(g_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + g_net_wm_state_hidden_atom = XInternAtom(g_display, "_NET_WM_STATE_HIDDEN", False); + 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_above_atom = XInternAtom(g_display, "_NET_WM_STATE_ABOVE", False); + g_net_wm_state_atom = XInternAtom(g_display, "_NET_WM_STATE", False); + g_net_wm_state_fullscreen_atom = XInternAtom(g_display, "_NET_WM_STATE_FULLSCREEN", 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); + g_net_wm_icon_atom = XInternAtom(g_display, "_NET_WM_ICON", False); + g_utf8_string_atom = XInternAtom(g_display, "UTF8_STRING", False); +} + + +/* + Get the window state: normal/minimized/maximized. +*/ +#ifndef MAKE_PROTO +int +ewmh_get_window_state(Window w) +{ + unsigned long nitems_return; + unsigned char *prop_return; + uint32 *return_words; + unsigned long item; + Bool maximized_vert, maximized_horz, hidden; + + maximized_vert = maximized_horz = hidden = False; + + if (get_property_value(w, "_NET_WM_STATE", 64, &nitems_return, &prop_return, 0) < 0) + return SEAMLESSRDP_NORMAL; + + return_words = (uint32 *) prop_return; + + for (item = 0; item < nitems_return; item++) + { + if (return_words[item] == g_net_wm_state_maximized_vert_atom) + maximized_vert = True; + if (return_words[item] == g_net_wm_state_maximized_horz_atom) + maximized_horz = True; + if (return_words[item] == g_net_wm_state_hidden_atom) + hidden = True; + } + + XFree(prop_return); + + if (maximized_vert && maximized_horz) + return SEAMLESSRDP_MAXIMIZED; + else if (hidden) + return SEAMLESSRDP_MINIMIZED; + else + return SEAMLESSRDP_NORMAL; +} + +static int +ewmh_modify_state(Window wnd, Bool add, Atom atom1, Atom atom2) +{ + Status status; + XEvent xevent; + + int result; + unsigned long nitems; + unsigned char *props; + uint32 state = WithdrawnState; + + /* The spec states that the window manager must respect any + _NET_WM_STATE attributes on a withdrawn window. In order words, we + modify the attributes directly for withdrawn windows and ask the WM + to do it for active windows. */ + result = get_property_value(wnd, "WM_STATE", 64, &nitems, &props, 1); + if ((result >= 0) && nitems) + { + state = *(uint32 *) props; + XFree(props); + } + + if (state == WithdrawnState) + { + if (add) + { + Atom atoms[2]; + + atoms[0] = atom1; + nitems = 1; + if (atom2) + { + atoms[1] = atom2; + nitems = 2; + } + + XChangeProperty(g_display, wnd, g_net_wm_state_atom, XA_ATOM, + 32, PropModeAppend, (unsigned char *) atoms, nitems); + } + else + { + Atom *atoms; + int i; + + if (get_property_value(wnd, "_NET_WM_STATE", 64, &nitems, &props, 1) < 0) + return 0; + + atoms = (Atom *) props; + + for (i = 0; i < nitems; i++) + { + if ((atoms[i] == atom1) || (atom2 && (atoms[i] == atom2))) + { + if (i != (nitems - 1)) + memmove(&atoms[i], &atoms[i + 1], + sizeof(Atom) * (nitems - i - 1)); + nitems--; + i--; + } + } + + XChangeProperty(g_display, wnd, g_net_wm_state_atom, XA_ATOM, + 32, PropModeReplace, (unsigned char *) atoms, nitems); + + XFree(props); + } + + return 0; + } + + xevent.type = ClientMessage; + xevent.xclient.window = wnd; + xevent.xclient.message_type = g_net_wm_state_atom; + xevent.xclient.format = 32; + if (add) + xevent.xclient.data.l[0] = _NET_WM_STATE_ADD; + else + xevent.xclient.data.l[0] = _NET_WM_STATE_REMOVE; + xevent.xclient.data.l[1] = atom1; + xevent.xclient.data.l[2] = atom2; + xevent.xclient.data.l[3] = 0; + xevent.xclient.data.l[4] = 0; + status = XSendEvent(g_display, DefaultRootWindow(g_display), False, + SubstructureNotifyMask | SubstructureRedirectMask, &xevent); + if (!status) + return -1; + + return 0; +} + + +int ewmh_state_fullscreen(Window wnd, Bool fullscreen) +{ + if (ewmh_modify_state(wnd, fullscreen, g_net_wm_state_fullscreen_atom, 0) < 0) + return -1; + return 0; +} + +/* + Set the window state: normal/minimized/maximized. + Returns -1 on failure. +*/ +int +ewmh_change_state(Window wnd, int state) +{ + /* + * Deal with the max atoms + */ + if (state == SEAMLESSRDP_MAXIMIZED) + { + if (ewmh_modify_state + (wnd, 1, g_net_wm_state_maximized_vert_atom, + g_net_wm_state_maximized_horz_atom) < 0) + return -1; + } + else + { + if (ewmh_modify_state + (wnd, 0, g_net_wm_state_maximized_vert_atom, + g_net_wm_state_maximized_horz_atom) < 0) + return -1; + } + + return 0; +} + + +int +ewmh_get_window_desktop(Window wnd) +{ + unsigned long nitems_return; + unsigned char *prop_return; + int desktop; + + if (get_property_value(wnd, "_NET_WM_DESKTOP", 1, &nitems_return, &prop_return, 0) < 0) + return (-1); + + if (nitems_return != 1) + { + fprintf(stderr, "_NET_WM_DESKTOP has bad length\n"); + return (-1); + } + + desktop = *prop_return; + XFree(prop_return); + return desktop; +} + + +int +ewmh_move_to_desktop(Window wnd, unsigned int desktop) +{ + Status status; + XEvent xevent; + + xevent.type = ClientMessage; + xevent.xclient.window = wnd; + xevent.xclient.message_type = g_net_wm_desktop_atom; + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = desktop; + xevent.xclient.data.l[1] = 0; + xevent.xclient.data.l[2] = 0; + xevent.xclient.data.l[3] = 0; + xevent.xclient.data.l[4] = 0; + status = XSendEvent(g_display, DefaultRootWindow(g_display), False, + SubstructureNotifyMask | SubstructureRedirectMask, &xevent); + if (!status) + return -1; + + return 0; +} + +void +ewmh_set_wm_name(Window wnd, const char *title) +{ + int len; + + len = strlen(title); + XChangeProperty(g_display, wnd, g_net_wm_name_atom, g_utf8_string_atom, + 8, PropModeReplace, (unsigned char *) title, len); +} + + +int +ewmh_set_window_popup(Window wnd) +{ + if (ewmh_modify_state + (wnd, 1, g_net_wm_state_skip_taskbar_atom, g_net_wm_state_skip_pager_atom) < 0) + return -1; + 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; +} + +void +ewmh_set_icon(Window wnd, int width, int height, const char *rgba_data) +{ + unsigned long nitems, i; + unsigned char *props; + unsigned long *cur_set, *new_set; + unsigned long *icon; + + cur_set = NULL; + new_set = NULL; + + if (get_property_value(wnd, "_NET_WM_ICON", 10000, &nitems, &props, 1) >= 0) + { + cur_set = (unsigned long *) props; + + for (i = 0; i < nitems;) + { + if (cur_set[i] == width && cur_set[i + 1] == height) + break; + + i += 2 + cur_set[i] * cur_set[i + 1]; + } + + if (i != nitems) + icon = cur_set + i; + else + { + new_set = xmalloc((nitems + width * height + 2) * sizeof(unsigned long)); + memcpy(new_set, cur_set, nitems * sizeof(unsigned long)); + icon = new_set + nitems; + nitems += width * height + 2; + } + } + else + { + new_set = xmalloc((width * height + 2) * sizeof(unsigned long)); + icon = new_set; + nitems = width * height + 2; + } + + icon[0] = width; + icon[1] = height; + + /* Convert RGBA -> ARGB */ + for (i = 0; i < width * height; i++) + { + icon[i + 2] = + rgba_data[i * 4 + 3] << 24 | + ((rgba_data[i * 4 + 0] << 16) & 0x00FF0000) | + ((rgba_data[i * 4 + 1] << 8) & 0x0000FF00) | + ((rgba_data[i * 4 + 2] << 0) & 0x000000FF); + } + + XChangeProperty(g_display, wnd, g_net_wm_icon_atom, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) (new_set ? new_set : cur_set), nitems); + + if (cur_set) + XFree(cur_set); + if (new_set) + free(new_set); +} + +void +ewmh_del_icon(Window wnd, int width, int height) +{ + unsigned long nitems, i, icon_size; + unsigned char *props; + unsigned long *cur_set, *new_set; + + cur_set = NULL; + new_set = NULL; + + if (get_property_value(wnd, "_NET_WM_ICON", 10000, &nitems, &props, 1) < 0) + return; + + cur_set = (unsigned long *) props; + + for (i = 0; i < nitems;) + { + if (cur_set[i] == width && cur_set[i + 1] == height) + break; + + i += 2 + cur_set[i] * cur_set[i + 1]; + } + + if (i == nitems) + goto out; + + icon_size = width * height + 2; + new_set = xmalloc((nitems - icon_size) * sizeof(unsigned long)); + + if (i != 0) + memcpy(new_set, cur_set, i * sizeof(unsigned long)); + if (i != nitems - icon_size) + memcpy(new_set + i, cur_set + i + icon_size, + (nitems - (i + icon_size)) * sizeof(unsigned long)); + + nitems -= icon_size; + + XChangeProperty(g_display, wnd, g_net_wm_icon_atom, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) new_set, nitems); + + free(new_set); + + out: + XFree(cur_set); +} + +int +ewmh_set_window_above(Window wnd) +{ + if (ewmh_modify_state(wnd, 1, g_net_wm_state_above_atom, 0) < 0) + return -1; + return 0; +} + +#endif /* MAKE_PROTO */ + + +#if 0 + +/* FIXME: _NET_MOVERESIZE_WINDOW is for pagers, not for + applications. We should implement _NET_WM_MOVERESIZE instead */ + +int +ewmh_net_moveresize_window(Window wnd, int x, int y, int width, int height) +{ + Status status; + XEvent xevent; + Atom moveresize; + + moveresize = XInternAtom(g_display, "_NET_MOVERESIZE_WINDOW", False); + if (!moveresize) + { + return -1; + } + + xevent.type = ClientMessage; + xevent.xclient.window = wnd; + xevent.xclient.message_type = moveresize; + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = StaticGravity | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11); + xevent.xclient.data.l[1] = x; + xevent.xclient.data.l[2] = y; + xevent.xclient.data.l[3] = width; + xevent.xclient.data.l[4] = height; + + status = XSendEvent(g_display, DefaultRootWindow(g_display), False, + SubstructureNotifyMask | SubstructureRedirectMask, &xevent); + if (!status) + return -1; + return 0; +} + +#endif Index: tigervnc/unix/vncviewer/Makefile.in =================================================================== --- tigervnc/unix/vncviewer/Makefile.in (revision 20144) +++ tigervnc/unix/vncviewer/Makefile.in (arbetskopia) @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -57,7 +57,8 @@ am__objects_1 = am_vncviewer_OBJECTS = $(am__objects_1) \ vncviewer-DesktopWindow.$(OBJEXT) vncviewer-CConn.$(OBJEXT) \ - vncviewer-vncviewer.$(OBJEXT) vncviewer-buildtime.$(OBJEXT) + vncviewer-vncviewer.$(OBJEXT) vncviewer-buildtime.$(OBJEXT) \ + vncviewer-ewmhints.$(OBJEXT) vncviewer_OBJECTS = $(am_vncviewer_OBJECTS) vncviewer_DEPENDENCIES = $(top_builddir)/unix/tx/libtx.la \ $(top_builddir)/common/rfb/librfb.la \ @@ -179,7 +180,6 @@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ POSUB = @POSUB@ @@ -260,7 +260,7 @@ OptionsDialog.h parameters.h PasswdDialog.h ServerDialog.h vncviewer_SOURCES = $(HDRS) DesktopWindow.cxx CConn.cxx vncviewer.cxx \ - buildtime.c + buildtime.c ewmhints.c # X_CFLAGS are really CPPFLAGS vncviewer_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/unix \ @@ -364,6 +364,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vncviewer-CConn.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vncviewer-DesktopWindow.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vncviewer-buildtime.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vncviewer-ewmhints.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vncviewer-vncviewer.Po@am__quote@ .c.o: @@ -401,6 +402,20 @@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(vncviewer_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o vncviewer-buildtime.obj `if test -f 'buildtime.c'; then $(CYGPATH_W) 'buildtime.c'; else $(CYGPATH_W) '$(srcdir)/buildtime.c'; fi` +vncviewer-ewmhints.o: ewmhints.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(vncviewer_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT vncviewer-ewmhints.o -MD -MP -MF $(DEPDIR)/vncviewer-ewmhints.Tpo -c -o vncviewer-ewmhints.o `test -f 'ewmhints.c' || echo '$(srcdir)/'`ewmhints.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/vncviewer-ewmhints.Tpo $(DEPDIR)/vncviewer-ewmhints.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ewmhints.c' object='vncviewer-ewmhints.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(vncviewer_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o vncviewer-ewmhints.o `test -f 'ewmhints.c' || echo '$(srcdir)/'`ewmhints.c + +vncviewer-ewmhints.obj: ewmhints.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(vncviewer_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT vncviewer-ewmhints.obj -MD -MP -MF $(DEPDIR)/vncviewer-ewmhints.Tpo -c -o vncviewer-ewmhints.obj `if test -f 'ewmhints.c'; then $(CYGPATH_W) 'ewmhints.c'; else $(CYGPATH_W) '$(srcdir)/ewmhints.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/vncviewer-ewmhints.Tpo $(DEPDIR)/vncviewer-ewmhints.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ewmhints.c' object='vncviewer-ewmhints.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(vncviewer_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o vncviewer-ewmhints.obj `if test -f 'ewmhints.c'; then $(CYGPATH_W) 'ewmhints.c'; else $(CYGPATH_W) '$(srcdir)/ewmhints.c'; fi` + .cxx.o: @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po Index: tigervnc/unix/vncviewer/ewmhints.h =================================================================== --- tigervnc/unix/vncviewer/ewmhints.h (revision 0) +++ tigervnc/unix/vncviewer/ewmhints.h (revision 0) @@ -0,0 +1,22 @@ +#ifdef __cplusplus +extern "C" { +#endif + +void ewmh_init(Display *display); +int ewmh_state_fullscreen(Window wnd, Bool fullscreen); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +void ewmh_init(Display *display); +int ewmh_state_fullscreen(Window wnd, Bool fullscreen); + +#ifdef __cplusplus +} +#endif + Index: tigervnc/unix/vncviewer/CConn.cxx =================================================================== --- tigervnc/unix/vncviewer/CConn.cxx (revision 20144) +++ tigervnc/unix/vncviewer/CConn.cxx (arbetskopia) @@ -42,6 +42,7 @@ #include "ServerDialog.h" #include "PasswdDialog.h" #include "parameters.h" +#include "ewmhints.h" #define SCANSCRIPT "/opt/thinlinc/lib/tlclient/local-scan" @@ -69,6 +70,7 @@ menuKeysym(0), menu(dpy, this), options(dpy, this), about(dpy), info(dpy), reverseConnection(reverse), firstUpdate(true), pendingUpdate(false) { + ewmh_init(dpy); CharArray menuKeyStr(menuKey.getData()); menuKeysym = XStringToKeysym(menuKeyStr.buf); @@ -754,6 +756,7 @@ } viewport->toplevel(windowNameStr.buf, this, argc, argv); viewport->setBumpScroll(fullScreen); +#if 0 XSetWindowAttributes attr; attr.override_redirect = fullScreen; XChangeWindowAttributes(dpy, viewport->win(), CWOverrideRedirect, &attr); @@ -761,15 +764,21 @@ XChangeWindowAttributes(dpy, options.win(), CWOverrideRedirect, &attr); XChangeWindowAttributes(dpy, about.win(), CWOverrideRedirect, &attr); XChangeWindowAttributes(dpy, info.win(), CWOverrideRedirect, &attr); - reconfigureViewport(); +#endif + + ewmh_state_fullscreen(viewport->win(), fullScreen); + + //reconfigureViewport(); ?? + menu.setTransientFor(viewport->win()); viewport->map(); - if (fullScreen) { - XGrabKeyboard(dpy, desktop->win(), True, GrabModeAsync, GrabModeAsync, - CurrentTime); - } else { - XUngrabKeyboard(dpy, CurrentTime); - } +// if (fullScreen) { +// XGrabKeyboard(dpy, desktop->win(), True, GrabModeAsync, GrabModeAsync, +// CurrentTime); +// } else { +// XUngrabKeyboard(dpy, CurrentTime); +// } +// if (oldViewport) delete oldViewport; } Index: tigervnc/unix/vncviewer/Makefile.am =================================================================== --- tigervnc/unix/vncviewer/Makefile.am (revision 20144) +++ tigervnc/unix/vncviewer/Makefile.am (arbetskopia) @@ -6,7 +6,7 @@ OptionsDialog.h parameters.h PasswdDialog.h ServerDialog.h vncviewer_SOURCES = $(HDRS) DesktopWindow.cxx CConn.cxx vncviewer.cxx \ - buildtime.c + buildtime.c ewmhints.c # X_CFLAGS are really CPPFLAGS vncviewer_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/unix \ -I$(top_srcdir)/unix/tx -I$(top_srcdir)/intl \