mirror of
https://github.com/gryf/wmaker.git
synced 2026-01-08 06:44:11 +01:00
In commit d6c134f420 ("Do not switch
workspace to follow new windows in others") the default behavior
was changed, and workspace switching to follow focus requests was
strictly forbidden.
Although that seems to be a sane thing to do by default, that raises
concerns about whether Window Maker could be more flexible in that
respect -- allowing the user to choose which applications are or
are not allowed to do that.
This patch adds such configuration, located in the "Advanced Options"
submenu of the top-level "Attributes" menu.
1651 lines
46 KiB
C
1651 lines
46 KiB
C
/* wmspec.c-- support for the wm-spec Hints
|
|
*
|
|
* Window Maker window manager
|
|
*
|
|
* Copyright (c) 1998-2003 Alfredo K. Kojima
|
|
*
|
|
* 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 2 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
* USA.
|
|
*/
|
|
|
|
/*
|
|
* TODO
|
|
* ----
|
|
*
|
|
* This file needs to be checked for all calls to XGetWindowProperty() and
|
|
* proper checks need to be made on the returned values. Only checking for
|
|
* return to be Success is not enough. -Dan
|
|
*/
|
|
|
|
#include "wconfig.h"
|
|
|
|
#ifdef NETWM_HINTS
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xarch.h>
|
|
#include <string.h>
|
|
|
|
#include "WindowMaker.h"
|
|
#include "window.h"
|
|
#include "screen.h"
|
|
#include "workspace.h"
|
|
#include "framewin.h"
|
|
#include "actions.h"
|
|
#include "client.h"
|
|
#include "wmspec.h"
|
|
#include "icon.h"
|
|
#include "stacking.h"
|
|
#include "xinerama.h"
|
|
#include "properties.h"
|
|
|
|
#ifdef DEBUG_WMSPEC
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
/* Global variables */
|
|
extern Atom _XA_WM_DELETE_WINDOW;
|
|
extern Time LastTimestamp;
|
|
extern WPreferences wPreferences;
|
|
|
|
/* Root Window Properties */
|
|
static Atom net_supported;
|
|
static Atom net_client_list;
|
|
static Atom net_client_list_stacking;
|
|
static Atom net_number_of_desktops;
|
|
static Atom net_desktop_geometry;
|
|
static Atom net_desktop_viewport;
|
|
static Atom net_current_desktop;
|
|
static Atom net_desktop_names;
|
|
static Atom net_active_window;
|
|
static Atom net_workarea; /* XXX: not xinerama compatible */
|
|
static Atom net_supporting_wm_check;
|
|
static Atom net_virtual_roots; /* N/A */
|
|
static Atom net_desktop_layout; /* XXX */
|
|
static Atom net_showing_desktop;
|
|
|
|
/* Other Root Window Messages */
|
|
static Atom net_close_window;
|
|
static Atom net_moveresize_window; /* TODO */
|
|
static Atom net_wm_moveresize; /* TODO */
|
|
|
|
/* Application Window Properties */
|
|
static Atom net_wm_name;
|
|
static Atom net_wm_visible_name; /* TODO (unnecessary?) */
|
|
static Atom net_wm_icon_name;
|
|
static Atom net_wm_visible_icon_name; /* TODO (unnecessary?) */
|
|
static Atom net_wm_desktop;
|
|
static Atom net_wm_window_type;
|
|
static Atom net_wm_window_type_desktop;
|
|
static Atom net_wm_window_type_dock;
|
|
static Atom net_wm_window_type_toolbar;
|
|
static Atom net_wm_window_type_menu;
|
|
static Atom net_wm_window_type_utility;
|
|
static Atom net_wm_window_type_splash;
|
|
static Atom net_wm_window_type_dialog;
|
|
static Atom net_wm_window_type_normal;
|
|
static Atom net_wm_state;
|
|
static Atom net_wm_state_modal; /* XXX: what is this?!? */
|
|
static Atom net_wm_state_sticky;
|
|
static Atom net_wm_state_maximized_vert;
|
|
static Atom net_wm_state_maximized_horz;
|
|
static Atom net_wm_state_shaded;
|
|
static Atom net_wm_state_skip_taskbar;
|
|
static Atom net_wm_state_skip_pager;
|
|
static Atom net_wm_state_hidden;
|
|
static Atom net_wm_state_fullscreen;
|
|
static Atom net_wm_state_above;
|
|
static Atom net_wm_state_below;
|
|
static Atom net_wm_allowed_actions;
|
|
static Atom net_wm_action_move;
|
|
static Atom net_wm_action_resize;
|
|
static Atom net_wm_action_minimize;
|
|
static Atom net_wm_action_shade;
|
|
static Atom net_wm_action_stick;
|
|
static Atom net_wm_action_maximize_horz;
|
|
static Atom net_wm_action_maximize_vert;
|
|
static Atom net_wm_action_fullscreen;
|
|
static Atom net_wm_action_change_desktop;
|
|
static Atom net_wm_action_close;
|
|
static Atom net_wm_strut; /* XXX: see net_workarea */
|
|
static Atom net_wm_strut_partial; /* TODO: doesn't really fit into the current strut scheme */
|
|
static Atom net_wm_icon_geometry; /* FIXME: should work together with net_wm_handled_icons, gnome-panel-2.2.0.1 doesn't use _NET_WM_HANDLED_ICONS, thus present situation. */
|
|
static Atom net_wm_icon;
|
|
static Atom net_wm_pid; /* TODO */
|
|
static Atom net_wm_handled_icons; /* FIXME: see net_wm_icon_geometry */
|
|
|
|
/* Window Manager Protocols */
|
|
static Atom net_wm_ping; /* TODO */
|
|
|
|
static Atom utf8_string;
|
|
|
|
typedef struct {
|
|
char *name;
|
|
Atom *atom;
|
|
} atomitem_t;
|
|
|
|
static atomitem_t atomNames[] = {
|
|
{"_NET_SUPPORTED", &net_supported},
|
|
{"_NET_CLIENT_LIST", &net_client_list},
|
|
{"_NET_CLIENT_LIST_STACKING", &net_client_list_stacking},
|
|
{"_NET_NUMBER_OF_DESKTOPS", &net_number_of_desktops},
|
|
{"_NET_DESKTOP_GEOMETRY", &net_desktop_geometry},
|
|
{"_NET_DESKTOP_VIEWPORT", &net_desktop_viewport},
|
|
{"_NET_CURRENT_DESKTOP", &net_current_desktop},
|
|
{"_NET_DESKTOP_NAMES", &net_desktop_names},
|
|
{"_NET_ACTIVE_WINDOW", &net_active_window},
|
|
{"_NET_WORKAREA", &net_workarea},
|
|
{"_NET_SUPPORTING_WM_CHECK", &net_supporting_wm_check},
|
|
{"_NET_VIRTUAL_ROOTS", &net_virtual_roots},
|
|
{"_NET_DESKTOP_LAYOUT", &net_desktop_layout},
|
|
{"_NET_SHOWING_DESKTOP", &net_showing_desktop},
|
|
|
|
{"_NET_CLOSE_WINDOW", &net_close_window},
|
|
{"_NET_MOVERESIZE_WINDOW", &net_moveresize_window},
|
|
{"_NET_WM_MOVERESIZE", &net_wm_moveresize},
|
|
|
|
{"_NET_WM_NAME", &net_wm_name},
|
|
{"_NET_WM_VISIBLE_NAME", &net_wm_visible_name},
|
|
{"_NET_WM_ICON_NAME", &net_wm_icon_name},
|
|
{"_NET_WM_VISIBLE_ICON_NAME", &net_wm_visible_icon_name},
|
|
{"_NET_WM_DESKTOP", &net_wm_desktop},
|
|
{"_NET_WM_WINDOW_TYPE", &net_wm_window_type},
|
|
{"_NET_WM_WINDOW_TYPE_DESKTOP", &net_wm_window_type_desktop},
|
|
{"_NET_WM_WINDOW_TYPE_DOCK", &net_wm_window_type_dock},
|
|
{"_NET_WM_WINDOW_TYPE_TOOLBAR", &net_wm_window_type_toolbar},
|
|
{"_NET_WM_WINDOW_TYPE_MENU", &net_wm_window_type_menu},
|
|
{"_NET_WM_WINDOW_TYPE_UTILITY", &net_wm_window_type_utility},
|
|
{"_NET_WM_WINDOW_TYPE_SPLASH", &net_wm_window_type_splash},
|
|
{"_NET_WM_WINDOW_TYPE_DIALOG", &net_wm_window_type_dialog},
|
|
{"_NET_WM_WINDOW_TYPE_NORMAL", &net_wm_window_type_normal},
|
|
{"_NET_WM_STATE", &net_wm_state},
|
|
{"_NET_WM_STATE_MODAL", &net_wm_state_modal},
|
|
{"_NET_WM_STATE_STICKY", &net_wm_state_sticky},
|
|
{"_NET_WM_STATE_MAXIMIZED_VERT", &net_wm_state_maximized_vert},
|
|
{"_NET_WM_STATE_MAXIMIZED_HORZ", &net_wm_state_maximized_horz},
|
|
{"_NET_WM_STATE_SHADED", &net_wm_state_shaded},
|
|
{"_NET_WM_STATE_SKIP_TASKBAR", &net_wm_state_skip_taskbar},
|
|
{"_NET_WM_STATE_SKIP_PAGER", &net_wm_state_skip_pager},
|
|
{"_NET_WM_STATE_HIDDEN", &net_wm_state_hidden},
|
|
{"_NET_WM_STATE_FULLSCREEN", &net_wm_state_fullscreen},
|
|
{"_NET_WM_STATE_ABOVE", &net_wm_state_above},
|
|
{"_NET_WM_STATE_BELOW", &net_wm_state_below},
|
|
{"_NET_WM_ALLOWED_ACTIONS", &net_wm_allowed_actions},
|
|
{"_NET_WM_ACTION_MOVE", &net_wm_action_move},
|
|
{"_NET_WM_ACTION_RESIZE", &net_wm_action_resize},
|
|
{"_NET_WM_ACTION_MINIMIZE", &net_wm_action_minimize},
|
|
{"_NET_WM_ACTION_SHADE", &net_wm_action_shade},
|
|
{"_NET_WM_ACTION_STICK", &net_wm_action_stick},
|
|
{"_NET_WM_ACTION_MAXIMIZE_HORZ", &net_wm_action_maximize_horz},
|
|
{"_NET_WM_ACTION_MAXIMIZE_VERT", &net_wm_action_maximize_vert},
|
|
{"_NET_WM_ACTION_FULLSCREEN", &net_wm_action_fullscreen},
|
|
{"_NET_WM_ACTION_CHANGE_DESKTOP", &net_wm_action_change_desktop},
|
|
{"_NET_WM_ACTION_CLOSE", &net_wm_action_close},
|
|
{"_NET_WM_STRUT", &net_wm_strut},
|
|
{"_NET_WM_STRUT_PARTIAL", &net_wm_strut_partial},
|
|
{"_NET_WM_ICON_GEOMETRY", &net_wm_icon_geometry},
|
|
{"_NET_WM_ICON", &net_wm_icon},
|
|
{"_NET_WM_PID", &net_wm_pid},
|
|
{"_NET_WM_HANDLED_ICONS", &net_wm_handled_icons},
|
|
|
|
{"_NET_WM_PING", &net_wm_ping},
|
|
|
|
{"UTF8_STRING", &utf8_string},
|
|
};
|
|
|
|
#define atomNr (sizeof(atomNames)/sizeof(atomitem_t))
|
|
|
|
#define _NET_WM_STATE_REMOVE 0
|
|
#define _NET_WM_STATE_ADD 1
|
|
#define _NET_WM_STATE_TOGGLE 2
|
|
|
|
#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 */
|
|
|
|
static void observer(void *self, WMNotification * notif);
|
|
static void wsobserver(void *self, WMNotification * notif);
|
|
|
|
static void updateClientList(WScreen * scr);
|
|
static void updateClientListStacking(WScreen * scr, WWindow *);
|
|
|
|
static void updateWorkspaceNames(WScreen * scr);
|
|
static void updateCurrentWorkspace(WScreen * scr);
|
|
static void updateWorkspaceCount(WScreen * scr);
|
|
|
|
typedef struct NetData {
|
|
WScreen *scr;
|
|
WReservedArea *strut;
|
|
WWindow **show_desktop;
|
|
} NetData;
|
|
|
|
static void setSupportedHints(WScreen * scr)
|
|
{
|
|
Atom atom[atomNr];
|
|
int i = 0;
|
|
|
|
/* set supported hints list */
|
|
/* XXX: extend this !!! */
|
|
|
|
atom[i++] = net_client_list;
|
|
atom[i++] = net_client_list_stacking;
|
|
atom[i++] = net_number_of_desktops;
|
|
atom[i++] = net_desktop_geometry;
|
|
atom[i++] = net_desktop_viewport;
|
|
atom[i++] = net_current_desktop;
|
|
atom[i++] = net_desktop_names;
|
|
atom[i++] = net_active_window;
|
|
atom[i++] = net_workarea;
|
|
atom[i++] = net_supporting_wm_check;
|
|
atom[i++] = net_showing_desktop;
|
|
#if 0
|
|
atom[i++] = net_wm_moveresize;
|
|
#endif
|
|
atom[i++] = net_wm_desktop;
|
|
atom[i++] = net_wm_window_type;
|
|
atom[i++] = net_wm_window_type_desktop;
|
|
atom[i++] = net_wm_window_type_dock;
|
|
atom[i++] = net_wm_window_type_toolbar;
|
|
atom[i++] = net_wm_window_type_menu;
|
|
atom[i++] = net_wm_window_type_utility;
|
|
atom[i++] = net_wm_window_type_splash;
|
|
atom[i++] = net_wm_window_type_dialog;
|
|
atom[i++] = net_wm_window_type_normal;
|
|
|
|
atom[i++] = net_wm_state;
|
|
/* atom[i++] = net_wm_state_modal; *//* XXX: not sure where/when to use it. */
|
|
atom[i++] = net_wm_state_sticky;
|
|
atom[i++] = net_wm_state_shaded;
|
|
atom[i++] = net_wm_state_maximized_horz;
|
|
atom[i++] = net_wm_state_maximized_vert;
|
|
atom[i++] = net_wm_state_skip_taskbar;
|
|
atom[i++] = net_wm_state_skip_pager;
|
|
atom[i++] = net_wm_state_hidden;
|
|
atom[i++] = net_wm_state_fullscreen;
|
|
atom[i++] = net_wm_state_above;
|
|
atom[i++] = net_wm_state_below;
|
|
|
|
atom[i++] = net_wm_allowed_actions;
|
|
atom[i++] = net_wm_action_move;
|
|
atom[i++] = net_wm_action_resize;
|
|
atom[i++] = net_wm_action_minimize;
|
|
atom[i++] = net_wm_action_shade;
|
|
atom[i++] = net_wm_action_stick;
|
|
atom[i++] = net_wm_action_maximize_horz;
|
|
atom[i++] = net_wm_action_maximize_vert;
|
|
atom[i++] = net_wm_action_fullscreen;
|
|
atom[i++] = net_wm_action_change_desktop;
|
|
atom[i++] = net_wm_action_close;
|
|
|
|
atom[i++] = net_wm_strut;
|
|
atom[i++] = net_wm_icon_geometry;
|
|
atom[i++] = net_wm_icon;
|
|
atom[i++] = net_wm_handled_icons;
|
|
|
|
atom[i++] = net_wm_name;
|
|
atom[i++] = net_wm_icon_name;
|
|
|
|
XChangeProperty(dpy, scr->root_win, net_supported, XA_ATOM, 32, PropModeReplace, (unsigned char *)atom, i);
|
|
|
|
/* set supporting wm hint */
|
|
XChangeProperty(dpy, scr->root_win, net_supporting_wm_check, XA_WINDOW, 32,
|
|
PropModeReplace, (unsigned char *)&scr->info_window, 1);
|
|
|
|
XChangeProperty(dpy, scr->info_window, net_supporting_wm_check, XA_WINDOW,
|
|
32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
|
|
}
|
|
|
|
void wNETWMUpdateDesktop(WScreen * scr)
|
|
{
|
|
long *views, sizes[2];
|
|
int count, i;
|
|
|
|
if (scr->workspace_count == 0)
|
|
return;
|
|
|
|
count = scr->workspace_count * 2;
|
|
views = wmalloc(sizeof(long) * count);
|
|
/*memset(views, 0, sizeof(long) * count); */
|
|
|
|
#ifdef VIRTUAL_DESKTOP
|
|
sizes[0] = scr->workspaces[scr->current_workspace]->width;
|
|
sizes[1] = scr->workspaces[scr->current_workspace]->height;
|
|
#else
|
|
sizes[0] = scr->scr_width;
|
|
sizes[1] = scr->scr_height;
|
|
#endif
|
|
|
|
for (i = 0; i < scr->workspace_count; i++) {
|
|
#ifdef VIRTUAL_DESKTOP
|
|
views[2 * i + 0] = scr->workspaces[i]->view_x;
|
|
views[2 * i + 1] = scr->workspaces[i]->view_y;
|
|
#else
|
|
views[2 * i + 0] = 0;
|
|
views[2 * i + 1] = 0;
|
|
#endif
|
|
}
|
|
|
|
XChangeProperty(dpy, scr->root_win, net_desktop_geometry, XA_CARDINAL, 32,
|
|
PropModeReplace, (unsigned char *)sizes, 2);
|
|
|
|
XChangeProperty(dpy, scr->root_win, net_desktop_viewport, XA_CARDINAL, 32,
|
|
PropModeReplace, (unsigned char *)views, count);
|
|
|
|
wfree(views);
|
|
}
|
|
|
|
int wNETWMGetCurrentDesktopFromHint(WScreen * scr)
|
|
{
|
|
int count;
|
|
unsigned char *prop;
|
|
|
|
prop = PropGetCheckProperty(scr->root_win, net_current_desktop, XA_CARDINAL, 0, 1, &count);
|
|
if (prop) {
|
|
int desktop = *(long *)prop;
|
|
XFree(prop);
|
|
return desktop;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Find the best icon to be used by Window Maker for appicon/miniwindows.
|
|
* Currently the algorithm is to take the image with the size closest
|
|
* to icon_size x icon_size, but never bigger than that.
|
|
*
|
|
* This algorithm is very poorly implemented and needs to be redone (it can
|
|
* easily select images with very large widths and very small heights over
|
|
* square images, if the area of the former is closer to the desired one).
|
|
*
|
|
* The logic can also be changed to accept bigger images and scale them down.
|
|
*/
|
|
static unsigned long *findBestIcon(unsigned long *data, unsigned long items)
|
|
{
|
|
int size, wanted, d;
|
|
unsigned long i, distance;
|
|
unsigned long *icon;
|
|
|
|
/* better use only 75% of icon_size. For 64x64 this means 48x48
|
|
* This leaves room around the icon for the miniwindow title and
|
|
* results in better overall aesthetics -Dan */
|
|
wanted = wPreferences.icon_size * wPreferences.icon_size;
|
|
|
|
for (icon = NULL, distance = wanted, i = 0L; i < items - 1;) {
|
|
size = data[i] * data[i + 1];
|
|
if (size == 0)
|
|
break;
|
|
d = wanted - size;
|
|
if (d >= 0 && d <= distance && (i + size + 2) <= items) {
|
|
distance = d;
|
|
icon = &data[i];
|
|
}
|
|
i += size + 2;
|
|
}
|
|
|
|
return icon;
|
|
}
|
|
|
|
static RImage *makeRImageFromARGBData(unsigned long *data)
|
|
{
|
|
int size, width, height, i;
|
|
RImage *image;
|
|
unsigned char *imgdata;
|
|
unsigned long pixel;
|
|
|
|
width = data[0];
|
|
height = data[1];
|
|
size = width * height;
|
|
|
|
if (size == 0)
|
|
return NULL;
|
|
|
|
image = RCreateImage(width, height, True);
|
|
|
|
for (imgdata = image->data, i = 2; i < size + 2; i++, imgdata += 4) {
|
|
pixel = data[i];
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
imgdata[2] = (pixel >> 24) & 0xff; /* A */
|
|
imgdata[1] = (pixel >> 16) & 0xff; /* R */
|
|
imgdata[0] = (pixel >> 8) & 0xff; /* G */
|
|
imgdata[3] = (pixel >> 0) & 0xff; /* B */
|
|
#else /* Little endian */
|
|
imgdata[3] = (pixel >> 24) & 0xff; /* A */
|
|
imgdata[0] = (pixel >> 16) & 0xff; /* R */
|
|
imgdata[1] = (pixel >> 8) & 0xff; /* G */
|
|
imgdata[2] = (pixel >> 0) & 0xff; /* B */
|
|
#endif /* endianness */
|
|
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
static void updateIconImage(WScreen * scr, WWindow * wwin)
|
|
{
|
|
unsigned long *property, *data;
|
|
unsigned long items, rest;
|
|
Atom type;
|
|
int format;
|
|
RImage *image;
|
|
|
|
if (XGetWindowProperty(dpy, wwin->client_win, net_wm_icon, 0L, LONG_MAX,
|
|
False, XA_CARDINAL, &type, &format, &items, &rest,
|
|
(unsigned char **)&property) != Success || !property) {
|
|
return;
|
|
}
|
|
|
|
if (type != XA_CARDINAL || format != 32 || items < 2) {
|
|
XFree(property);
|
|
return;
|
|
}
|
|
|
|
data = findBestIcon(property, items);
|
|
if (!data) {
|
|
XFree(property);
|
|
return;
|
|
}
|
|
|
|
image = makeRImageFromARGBData(data);
|
|
|
|
if (image) {
|
|
if (wwin->net_icon_image)
|
|
RReleaseImage(wwin->net_icon_image);
|
|
wwin->net_icon_image = image;
|
|
}
|
|
|
|
XFree(property);
|
|
}
|
|
|
|
static void updateShowDesktop(WScreen * scr, Bool show)
|
|
{
|
|
long foo;
|
|
|
|
foo = (show == True);
|
|
XChangeProperty(dpy, scr->root_win, net_showing_desktop, XA_CARDINAL, 32,
|
|
PropModeReplace, (unsigned char *)&foo, 1);
|
|
}
|
|
|
|
void wNETWMShowingDesktop(WScreen * scr, Bool show)
|
|
{
|
|
if (show && scr->netdata->show_desktop == NULL) {
|
|
WWindow *tmp, **wins;
|
|
int i = 0;
|
|
|
|
wins = (WWindow **) wmalloc(sizeof(WWindow *) * (scr->window_count + 1));
|
|
|
|
tmp = scr->focused_window;
|
|
while (tmp) {
|
|
if (!tmp->flags.hidden && !tmp->flags.miniaturized && !WFLAGP(tmp, skip_window_list)) {
|
|
|
|
wins[i++] = tmp;
|
|
tmp->flags.skip_next_animation = 1;
|
|
tmp->flags.net_show_desktop = 1;
|
|
wIconifyWindow(tmp);
|
|
}
|
|
|
|
tmp = tmp->prev;
|
|
}
|
|
wins[i++] = NULL;
|
|
|
|
scr->netdata->show_desktop = wins;
|
|
updateShowDesktop(scr, True);
|
|
} else if (scr->netdata->show_desktop != NULL) {
|
|
/* FIXME: get rid of workspace flashing ! */
|
|
int ws = scr->current_workspace;
|
|
WWindow **tmp;
|
|
for (tmp = scr->netdata->show_desktop; *tmp; ++tmp) {
|
|
wDeiconifyWindow(*tmp);
|
|
(*tmp)->flags.net_show_desktop = 0;
|
|
}
|
|
if (ws != scr->current_workspace)
|
|
wWorkspaceChange(scr, ws);
|
|
wfree(scr->netdata->show_desktop);
|
|
scr->netdata->show_desktop = NULL;
|
|
updateShowDesktop(scr, False);
|
|
}
|
|
}
|
|
|
|
void wNETWMInitStuff(WScreen * scr)
|
|
{
|
|
NetData *data;
|
|
int i;
|
|
|
|
#ifdef DEBUG_WMSPEC
|
|
printf("wNETWMInitStuff\n");
|
|
#endif
|
|
|
|
#ifdef HAVE_XINTERNATOMS
|
|
{
|
|
Atom atoms[atomNr];
|
|
char *names[atomNr];
|
|
|
|
for (i = 0; i < atomNr; ++i) {
|
|
names[i] = atomNames[i].name;
|
|
}
|
|
XInternAtoms(dpy, &names[0], atomNr, False, atoms);
|
|
for (i = 0; i < atomNr; ++i) {
|
|
*atomNames[i].atom = atoms[i];
|
|
}
|
|
}
|
|
#else
|
|
for (i = 0; i < atomNr; i++) {
|
|
*atomNames[i].atom = XInternAtom(dpy, atomNames[i].name, False);
|
|
}
|
|
#endif
|
|
|
|
data = wmalloc(sizeof(NetData));
|
|
data->scr = scr;
|
|
data->strut = NULL;
|
|
data->show_desktop = NULL;
|
|
|
|
scr->netdata = data;
|
|
|
|
setSupportedHints(scr);
|
|
|
|
WMAddNotificationObserver(observer, data, WMNManaged, NULL);
|
|
WMAddNotificationObserver(observer, data, WMNUnmanaged, NULL);
|
|
WMAddNotificationObserver(observer, data, WMNChangedWorkspace, NULL);
|
|
WMAddNotificationObserver(observer, data, WMNChangedState, NULL);
|
|
WMAddNotificationObserver(observer, data, WMNChangedFocus, NULL);
|
|
WMAddNotificationObserver(observer, data, WMNChangedStacking, NULL);
|
|
WMAddNotificationObserver(observer, data, WMNChangedName, NULL);
|
|
|
|
WMAddNotificationObserver(wsobserver, data, WMNWorkspaceCreated, NULL);
|
|
WMAddNotificationObserver(wsobserver, data, WMNWorkspaceDestroyed, NULL);
|
|
WMAddNotificationObserver(wsobserver, data, WMNWorkspaceChanged, NULL);
|
|
WMAddNotificationObserver(wsobserver, data, WMNWorkspaceNameChanged, NULL);
|
|
|
|
updateClientList(scr);
|
|
updateClientListStacking(scr, NULL);
|
|
updateWorkspaceCount(scr);
|
|
updateWorkspaceNames(scr);
|
|
updateShowDesktop(scr, False);
|
|
|
|
wScreenUpdateUsableArea(scr);
|
|
}
|
|
|
|
void wNETWMCleanup(WScreen * scr)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < atomNr; i++)
|
|
XDeleteProperty(dpy, scr->root_win, *atomNames[i].atom);
|
|
}
|
|
|
|
void wNETWMUpdateActions(WWindow * wwin, Bool del)
|
|
{
|
|
Atom action[10]; /* nr of actions atoms defined */
|
|
int i = 0;
|
|
|
|
if (del) {
|
|
XDeleteProperty(dpy, wwin->client_win, net_wm_allowed_actions);
|
|
return;
|
|
}
|
|
|
|
if (IS_MOVABLE(wwin))
|
|
action[i++] = net_wm_action_move;
|
|
|
|
if (IS_RESIZABLE(wwin))
|
|
action[i++] = net_wm_action_resize;
|
|
|
|
if (!WFLAGP(wwin, no_miniaturizable))
|
|
action[i++] = net_wm_action_minimize;
|
|
|
|
if (!WFLAGP(wwin, no_shadeable))
|
|
action[i++] = net_wm_action_shade;
|
|
|
|
/* if (!WFLAGP(wwin, no_stickable)) */
|
|
action[i++] = net_wm_action_stick;
|
|
|
|
/* if (!(WFLAGP(wwin, no_maximizeable) & MAX_HORIZONTAL)) */
|
|
if (IS_RESIZABLE(wwin))
|
|
action[i++] = net_wm_action_maximize_horz;
|
|
|
|
/* if (!(WFLAGP(wwin, no_maximizeable) & MAX_VERTICAL)) */
|
|
if (IS_RESIZABLE(wwin))
|
|
action[i++] = net_wm_action_maximize_vert;
|
|
|
|
/* if (!WFLAGP(wwin, no_fullscreen)) */
|
|
action[i++] = net_wm_action_fullscreen;
|
|
|
|
/* if (!WFLAGP(wwin, no_change_desktop)) */
|
|
action[i++] = net_wm_action_change_desktop;
|
|
|
|
if (!WFLAGP(wwin, no_closable))
|
|
action[i++] = net_wm_action_close;
|
|
|
|
XChangeProperty(dpy, wwin->client_win, net_wm_allowed_actions,
|
|
XA_ATOM, 32, PropModeReplace, (unsigned char *)action, i);
|
|
}
|
|
|
|
void wNETWMUpdateWorkarea(WScreen * scr, WArea usableArea)
|
|
{
|
|
long *area;
|
|
int count, i;
|
|
|
|
/* XXX: not Xinerama compatible,
|
|
xinerama gets the largest available */
|
|
|
|
if (!scr->netdata || scr->workspace_count == 0)
|
|
return;
|
|
|
|
count = scr->workspace_count * 4;
|
|
area = wmalloc(sizeof(long) * count);
|
|
for (i = 0; i < scr->workspace_count; i++) {
|
|
area[4 * i + 0] = usableArea.x1;
|
|
area[4 * i + 1] = usableArea.y1;
|
|
area[4 * i + 2] = usableArea.x2 - usableArea.x1;
|
|
area[4 * i + 3] = usableArea.y2 - usableArea.y1;
|
|
}
|
|
|
|
XChangeProperty(dpy, scr->root_win, net_workarea, XA_CARDINAL, 32,
|
|
PropModeReplace, (unsigned char *)area, count);
|
|
|
|
wfree(area);
|
|
}
|
|
|
|
Bool wNETWMGetUsableArea(WScreen * scr, int head, WArea * area)
|
|
{
|
|
WReservedArea *cur;
|
|
WMRect rect;
|
|
|
|
if (!scr->netdata || !scr->netdata->strut)
|
|
return False;
|
|
|
|
area->x1 = area->y1 = area->x2 = area->y2 = 0;
|
|
|
|
for (cur = scr->netdata->strut; cur; cur = cur->next) {
|
|
WWindow *wwin = wWindowFor(cur->window);
|
|
if (wWindowTouchesHead(wwin, head)) {
|
|
if (cur->area.x1 > area->x1)
|
|
area->x1 = cur->area.x1;
|
|
if (cur->area.y1 > area->y1)
|
|
area->y1 = cur->area.y1;
|
|
if (cur->area.x2 > area->x2)
|
|
area->x2 = cur->area.x2;
|
|
if (cur->area.y2 > area->y2)
|
|
area->y2 = cur->area.y2;
|
|
}
|
|
}
|
|
|
|
if (area->x1 == 0 && area->x2 == 0 && area->y1 == 0 && area->y2 == 0)
|
|
return False;
|
|
|
|
rect = wGetRectForHead(scr, head);
|
|
|
|
area->x1 = rect.pos.x + area->x1;
|
|
area->x2 = rect.pos.x + rect.size.width - area->x2;
|
|
area->y1 = rect.pos.y + area->y1;
|
|
area->y2 = rect.pos.y + rect.size.height - area->y2;
|
|
|
|
return True;
|
|
}
|
|
|
|
static void updateClientList(WScreen * scr)
|
|
{
|
|
WWindow *wwin;
|
|
Window *windows;
|
|
int count;
|
|
|
|
windows = (Window *) wmalloc(sizeof(Window) * (scr->window_count + 1));
|
|
|
|
count = 0;
|
|
wwin = scr->focused_window;
|
|
while (wwin) {
|
|
windows[count++] = wwin->client_win;
|
|
wwin = wwin->prev;
|
|
}
|
|
XChangeProperty(dpy, scr->root_win, net_client_list, XA_WINDOW, 32,
|
|
PropModeReplace, (unsigned char *)windows, count);
|
|
|
|
wfree(windows);
|
|
XFlush(dpy);
|
|
}
|
|
|
|
static void updateClientListStacking(WScreen * scr, WWindow * wwin_excl)
|
|
{
|
|
WWindow *wwin;
|
|
Window *client_list;
|
|
Window *client_list_reverse;
|
|
int client_count;
|
|
WCoreWindow *tmp;
|
|
WMBagIterator iter;
|
|
int i;
|
|
|
|
/* update client list */
|
|
i = scr->window_count + 1;
|
|
client_list = (Window *) wmalloc(sizeof(Window) * i);
|
|
client_list_reverse = (Window *) wmalloc(sizeof(Window) * i);
|
|
|
|
client_count = 0;
|
|
WM_ETARETI_BAG(scr->stacking_list, tmp, iter) {
|
|
while (tmp) {
|
|
wwin = wWindowFor(tmp->window);
|
|
/* wwin_excl is a window to exclude from the list
|
|
(e.g. it's now unmanaged) */
|
|
if (wwin && (wwin != wwin_excl))
|
|
client_list[client_count++] = wwin->client_win;
|
|
tmp = tmp->stacking->under;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < client_count; i++) {
|
|
Window w = client_list[client_count - i - 1];
|
|
client_list_reverse[i] = w;
|
|
}
|
|
|
|
XChangeProperty(dpy, scr->root_win, net_client_list_stacking, XA_WINDOW, 32,
|
|
PropModeReplace, (unsigned char *)client_list_reverse, client_count);
|
|
|
|
wfree(client_list);
|
|
wfree(client_list_reverse);
|
|
|
|
XFlush(dpy);
|
|
}
|
|
|
|
static void updateWorkspaceCount(WScreen * scr)
|
|
{ /* changeable */
|
|
long count;
|
|
|
|
count = scr->workspace_count;
|
|
|
|
XChangeProperty(dpy, scr->root_win, net_number_of_desktops, XA_CARDINAL,
|
|
32, PropModeReplace, (unsigned char *)&count, 1);
|
|
}
|
|
|
|
static void updateCurrentWorkspace(WScreen * scr)
|
|
{ /* changeable */
|
|
long count;
|
|
|
|
count = scr->current_workspace;
|
|
|
|
XChangeProperty(dpy, scr->root_win, net_current_desktop, XA_CARDINAL, 32,
|
|
PropModeReplace, (unsigned char *)&count, 1);
|
|
}
|
|
|
|
static void updateWorkspaceNames(WScreen * scr)
|
|
{
|
|
char buf[MAX_WORKSPACES * (MAX_WORKSPACENAME_WIDTH + 1)], *pos;
|
|
unsigned int i, len, curr_size;
|
|
|
|
pos = buf;
|
|
len = 0;
|
|
for (i = 0; i < scr->workspace_count; i++) {
|
|
curr_size = strlen(scr->workspaces[i]->name);
|
|
strcpy(pos, scr->workspaces[i]->name);
|
|
pos += (curr_size + 1);
|
|
len += (curr_size + 1);
|
|
}
|
|
|
|
XChangeProperty(dpy, scr->root_win, net_desktop_names, utf8_string, 8,
|
|
PropModeReplace, (unsigned char *)buf, len);
|
|
}
|
|
|
|
static void updateFocusHint(WScreen * scr, WWindow * wwin)
|
|
{ /* changeable */
|
|
Window window;
|
|
|
|
if (!scr->focused_window || !scr->focused_window->flags.focused)
|
|
window = None;
|
|
else
|
|
window = scr->focused_window->client_win;
|
|
|
|
XChangeProperty(dpy, scr->root_win, net_active_window, XA_WINDOW, 32,
|
|
PropModeReplace, (unsigned char *)&window, 1);
|
|
}
|
|
|
|
static void updateWorkspaceHint(WWindow * wwin, Bool fake, Bool del)
|
|
{
|
|
long l;
|
|
|
|
if (del) {
|
|
XDeleteProperty(dpy, wwin->client_win, net_wm_desktop);
|
|
} else {
|
|
l = ((fake || IS_OMNIPRESENT(wwin)) ? -1 : wwin->frame->workspace);
|
|
XChangeProperty(dpy, wwin->client_win, net_wm_desktop, XA_CARDINAL,
|
|
32, PropModeReplace, (unsigned char *)&l, 1);
|
|
}
|
|
}
|
|
|
|
static void updateStateHint(WWindow * wwin, Bool changedWorkspace, Bool del)
|
|
{ /* changeable */
|
|
if (del) {
|
|
if (!wwin->flags.net_state_from_client) {
|
|
XDeleteProperty(dpy, wwin->client_win, net_wm_state);
|
|
}
|
|
} else {
|
|
Atom state[15]; /* nr of defined state atoms */
|
|
int i = 0;
|
|
|
|
if (changedWorkspace || (wPreferences.sticky_icons && !IS_OMNIPRESENT(wwin)))
|
|
updateWorkspaceHint(wwin, False, False);
|
|
|
|
if (IS_OMNIPRESENT(wwin))
|
|
state[i++] = net_wm_state_sticky;
|
|
if (wwin->flags.shaded)
|
|
state[i++] = net_wm_state_shaded;
|
|
if (wwin->flags.maximized & MAX_HORIZONTAL)
|
|
state[i++] = net_wm_state_maximized_horz;
|
|
if (wwin->flags.maximized & MAX_VERTICAL)
|
|
state[i++] = net_wm_state_maximized_vert;
|
|
if (WFLAGP(wwin, skip_window_list))
|
|
state[i++] = net_wm_state_skip_taskbar;
|
|
if (wwin->flags.net_skip_pager)
|
|
state[i++] = net_wm_state_skip_pager;
|
|
if ((wwin->flags.hidden || wwin->flags.miniaturized) && !wwin->flags.net_show_desktop) {
|
|
state[i++] = net_wm_state_hidden;
|
|
state[i++] = net_wm_state_skip_pager;
|
|
|
|
if (wwin->flags.miniaturized && wPreferences.sticky_icons) {
|
|
if (!IS_OMNIPRESENT(wwin))
|
|
updateWorkspaceHint(wwin, True, False);
|
|
state[i++] = net_wm_state_sticky;
|
|
}
|
|
}
|
|
if (WFLAGP(wwin, sunken))
|
|
state[i++] = net_wm_state_below;
|
|
if (WFLAGP(wwin, floating))
|
|
state[i++] = net_wm_state_above;
|
|
if (wwin->flags.fullscreen)
|
|
state[i++] = net_wm_state_fullscreen;
|
|
|
|
XChangeProperty(dpy, wwin->client_win, net_wm_state, XA_ATOM, 32,
|
|
PropModeReplace, (unsigned char *)state, i);
|
|
}
|
|
}
|
|
|
|
static Bool updateStrut(WWindow * wwin, Bool adding)
|
|
{
|
|
WScreen *scr;
|
|
WReservedArea *area;
|
|
Bool hasState = False;
|
|
|
|
scr = wwin->screen_ptr;
|
|
|
|
if (adding) {
|
|
Atom type_ret;
|
|
int fmt_ret;
|
|
unsigned long nitems_ret;
|
|
unsigned long bytes_after_ret;
|
|
long *data = 0;
|
|
|
|
if (XGetWindowProperty(dpy, wwin->client_win, net_wm_strut, 0, 4, False,
|
|
XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
|
|
&bytes_after_ret, (unsigned char **)&data) == Success && data) {
|
|
|
|
area = (WReservedArea *) wmalloc(sizeof(WReservedArea));
|
|
area->area.x1 = data[0];
|
|
area->area.x2 = data[1];
|
|
area->area.y1 = data[2];
|
|
area->area.y2 = data[3];
|
|
|
|
area->window = wwin->client_win;
|
|
|
|
area->next = scr->netdata->strut;
|
|
scr->netdata->strut = area;
|
|
|
|
XFree(data);
|
|
hasState = True;
|
|
#ifdef VIRTUAL_DESKTOP
|
|
/* just in case wm_window_type didn't set it already */
|
|
wwin->client_flags.virtual_stick = 1;
|
|
#endif
|
|
}
|
|
|
|
} else {
|
|
/* deleting */
|
|
area = scr->netdata->strut;
|
|
|
|
if (area) {
|
|
if (area->window == wwin->client_win) {
|
|
scr->netdata->strut = area->next;
|
|
wfree(area);
|
|
hasState = True;
|
|
} else {
|
|
while (area->next && area->next->window != wwin->client_win)
|
|
area = area->next;
|
|
|
|
if (area->next) {
|
|
WReservedArea *next;
|
|
|
|
next = area->next->next;
|
|
wfree(area->next);
|
|
area->next = next;
|
|
|
|
hasState = True;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hasState;
|
|
}
|
|
|
|
static int getWindowLayer(WWindow * wwin)
|
|
{
|
|
int layer = WMNormalLevel;
|
|
|
|
if (wwin->type == net_wm_window_type_desktop) {
|
|
layer = WMDesktopLevel;
|
|
} else if (wwin->type == net_wm_window_type_dock) {
|
|
layer = WMDockLevel;
|
|
} else if (wwin->type == net_wm_window_type_toolbar) {
|
|
layer = WMMainMenuLevel;
|
|
} else if (wwin->type == net_wm_window_type_menu) {
|
|
layer = WMSubmenuLevel;
|
|
} else if (wwin->type == net_wm_window_type_utility) {
|
|
} else if (wwin->type == net_wm_window_type_splash) {
|
|
} else if (wwin->type == net_wm_window_type_dialog) {
|
|
if (wwin->transient_for) {
|
|
WWindow *parent = wWindowFor(wwin->transient_for);
|
|
if (parent && parent->flags.fullscreen) {
|
|
layer = WMFullscreenLevel;
|
|
}
|
|
}
|
|
/* //layer = WMPopUpLevel; // this seems a bad idea -Dan */
|
|
} else if (wwin->type == net_wm_window_type_normal) {
|
|
}
|
|
|
|
if (wwin->client_flags.sunken && WMSunkenLevel < layer)
|
|
layer = WMSunkenLevel;
|
|
if (wwin->client_flags.floating && WMFloatingLevel > layer)
|
|
layer = WMFloatingLevel;
|
|
|
|
return layer;
|
|
}
|
|
|
|
static void doStateAtom(WWindow * wwin, Atom state, int set, Bool init)
|
|
{
|
|
|
|
if (state == net_wm_state_sticky) {
|
|
if (set == _NET_WM_STATE_TOGGLE) {
|
|
set = !IS_OMNIPRESENT(wwin);
|
|
}
|
|
if (set != wwin->flags.omnipresent) {
|
|
wWindowSetOmnipresent(wwin, set);
|
|
}
|
|
} else if (state == net_wm_state_shaded) {
|
|
if (set == _NET_WM_STATE_TOGGLE) {
|
|
set = !wwin->flags.shaded;
|
|
}
|
|
if (init) {
|
|
wwin->flags.shaded = set;
|
|
} else {
|
|
if (set) {
|
|
wShadeWindow(wwin);
|
|
} else {
|
|
wUnshadeWindow(wwin);
|
|
}
|
|
}
|
|
} else if (state == net_wm_state_skip_taskbar) {
|
|
if (set == _NET_WM_STATE_TOGGLE) {
|
|
set = !wwin->client_flags.skip_window_list;
|
|
}
|
|
wwin->client_flags.skip_window_list = set;
|
|
} else if (state == net_wm_state_skip_pager) {
|
|
if (set == _NET_WM_STATE_TOGGLE) {
|
|
set = !wwin->flags.net_skip_pager;
|
|
}
|
|
wwin->flags.net_skip_pager = set;
|
|
} else if (state == net_wm_state_maximized_vert) {
|
|
if (set == _NET_WM_STATE_TOGGLE) {
|
|
set = !(wwin->flags.maximized & MAX_VERTICAL);
|
|
}
|
|
if (init) {
|
|
wwin->flags.maximized |= (set ? MAX_VERTICAL : 0);
|
|
} else {
|
|
if (set) {
|
|
wMaximizeWindow(wwin, wwin->flags.maximized | MAX_VERTICAL);
|
|
} else {
|
|
wMaximizeWindow(wwin, wwin->flags.maximized & ~MAX_VERTICAL);
|
|
}
|
|
}
|
|
} else if (state == net_wm_state_maximized_horz) {
|
|
if (set == _NET_WM_STATE_TOGGLE) {
|
|
set = !(wwin->flags.maximized & MAX_HORIZONTAL);
|
|
}
|
|
if (init) {
|
|
wwin->flags.maximized |= (set ? MAX_HORIZONTAL : 0);
|
|
} else {
|
|
if (set) {
|
|
wMaximizeWindow(wwin, wwin->flags.maximized | MAX_HORIZONTAL);
|
|
} else {
|
|
wMaximizeWindow(wwin, wwin->flags.maximized & ~MAX_HORIZONTAL);
|
|
}
|
|
}
|
|
} else if (state == net_wm_state_hidden) {
|
|
if (set == _NET_WM_STATE_TOGGLE) {
|
|
set = !(wwin->flags.miniaturized);
|
|
}
|
|
if (init) {
|
|
wwin->flags.miniaturized = set;
|
|
} else {
|
|
if (set) {
|
|
wIconifyWindow(wwin);
|
|
} else {
|
|
wDeiconifyWindow(wwin);
|
|
}
|
|
}
|
|
} else if (state == net_wm_state_fullscreen) {
|
|
if (set == _NET_WM_STATE_TOGGLE) {
|
|
set = !(wwin->flags.fullscreen);
|
|
}
|
|
if (init) {
|
|
wwin->flags.fullscreen = set;
|
|
} else {
|
|
if (set) {
|
|
wFullscreenWindow(wwin);
|
|
} else {
|
|
wUnfullscreenWindow(wwin);
|
|
}
|
|
}
|
|
} else if (state == net_wm_state_above) {
|
|
if (set == _NET_WM_STATE_TOGGLE) {
|
|
set = !(wwin->client_flags.floating);
|
|
}
|
|
if (init) {
|
|
wwin->client_flags.floating = set;
|
|
} else {
|
|
wwin->client_flags.floating = set;
|
|
ChangeStackingLevel(wwin->frame->core, getWindowLayer(wwin));
|
|
}
|
|
|
|
} else if (state == net_wm_state_below) {
|
|
if (set == _NET_WM_STATE_TOGGLE) {
|
|
set = !(wwin->client_flags.sunken);
|
|
}
|
|
if (init) {
|
|
wwin->client_flags.sunken = set;
|
|
} else {
|
|
wwin->client_flags.sunken = set;
|
|
ChangeStackingLevel(wwin->frame->core, getWindowLayer(wwin));
|
|
}
|
|
|
|
} else {
|
|
#ifdef DEBUG_WMSPEC
|
|
printf("doStateAtom unknown atom %s set %d\n", XGetAtomName(dpy, state), set);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void removeIcon(WWindow * wwin)
|
|
{
|
|
if (wwin->icon == NULL)
|
|
return;
|
|
if (wwin->flags.miniaturized && wwin->icon->mapped) {
|
|
XUnmapWindow(dpy, wwin->icon->core->window);
|
|
RemoveFromStackList(wwin->icon->core);
|
|
wIconDestroy(wwin->icon);
|
|
wwin->icon = NULL;
|
|
}
|
|
}
|
|
|
|
static Bool handleWindowType(WWindow * wwin, Atom type, int *layer)
|
|
{
|
|
Bool ret = True;
|
|
|
|
if (type == net_wm_window_type_desktop) {
|
|
wwin->client_flags.no_titlebar = 1;
|
|
wwin->client_flags.no_resizable = 1;
|
|
wwin->client_flags.no_miniaturizable = 1;
|
|
wwin->client_flags.no_border = 1;
|
|
wwin->client_flags.no_resizebar = 1;
|
|
wwin->client_flags.no_shadeable = 1;
|
|
wwin->client_flags.no_movable = 1;
|
|
wwin->client_flags.omnipresent = 1;
|
|
wwin->client_flags.skip_window_list = 1;
|
|
wwin->client_flags.dont_move_off = 1;
|
|
wwin->client_flags.no_appicon = 1;
|
|
wwin->flags.net_skip_pager = 1;
|
|
wwin->frame_x = 0;
|
|
wwin->frame_y = 0;
|
|
#ifdef VIRTUAL_DESKTOP
|
|
wwin->client_flags.virtual_stick = 1;
|
|
#endif
|
|
} else if (type == net_wm_window_type_dock) {
|
|
wwin->client_flags.no_titlebar = 1;
|
|
wwin->client_flags.no_resizable = 1;
|
|
wwin->client_flags.no_miniaturizable = 1;
|
|
wwin->client_flags.no_border = 1; /* XXX: really not a single decoration. */
|
|
wwin->client_flags.no_resizebar = 1;
|
|
wwin->client_flags.no_shadeable = 1;
|
|
wwin->client_flags.no_movable = 1;
|
|
wwin->client_flags.omnipresent = 1;
|
|
wwin->client_flags.skip_window_list = 1;
|
|
wwin->client_flags.dont_move_off = 1;
|
|
wwin->flags.net_skip_pager = 1;
|
|
#ifdef VIRTUAL_DESKTOP
|
|
wwin->client_flags.virtual_stick = 1;
|
|
#endif
|
|
} else if (type == net_wm_window_type_toolbar) {
|
|
wwin->client_flags.no_titlebar = 1;
|
|
wwin->client_flags.no_resizable = 1;
|
|
wwin->client_flags.no_miniaturizable = 1;
|
|
wwin->client_flags.no_resizebar = 1;
|
|
wwin->client_flags.no_shadeable = 1;
|
|
wwin->client_flags.skip_window_list = 1;
|
|
wwin->client_flags.dont_move_off = 1;
|
|
wwin->client_flags.no_appicon = 1;
|
|
} else if (type == net_wm_window_type_menu) {
|
|
wwin->client_flags.no_titlebar = 1;
|
|
wwin->client_flags.no_resizable = 1;
|
|
wwin->client_flags.no_miniaturizable = 1;
|
|
wwin->client_flags.no_resizebar = 1;
|
|
wwin->client_flags.no_shadeable = 1;
|
|
wwin->client_flags.skip_window_list = 1;
|
|
wwin->client_flags.dont_move_off = 1;
|
|
wwin->client_flags.no_appicon = 1;
|
|
} else if (type == net_wm_window_type_utility) {
|
|
wwin->client_flags.no_appicon = 1;
|
|
} else if (type == net_wm_window_type_splash) {
|
|
wwin->client_flags.no_titlebar = 1;
|
|
wwin->client_flags.no_resizable = 1;
|
|
wwin->client_flags.no_miniaturizable = 1;
|
|
wwin->client_flags.no_resizebar = 1;
|
|
wwin->client_flags.no_shadeable = 1;
|
|
wwin->client_flags.no_movable = 1;
|
|
wwin->client_flags.skip_window_list = 1;
|
|
wwin->client_flags.dont_move_off = 1;
|
|
wwin->client_flags.no_appicon = 1;
|
|
wwin->flags.net_skip_pager = 1;
|
|
} else if (type == net_wm_window_type_dialog) {
|
|
/* These also seem a bad idea in our context -Dan
|
|
// wwin->client_flags.skip_window_list = 1;
|
|
// wwin->client_flags.no_appicon = 1;
|
|
*/
|
|
} else if (type == net_wm_window_type_normal) {
|
|
} else {
|
|
ret = False;
|
|
}
|
|
|
|
wwin->type = type;
|
|
*layer = getWindowLayer(wwin);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void wNETWMPositionSplash(WWindow * wwin, int *x, int *y, int width, int height)
|
|
{
|
|
if (wwin->type == net_wm_window_type_splash) {
|
|
WScreen *scr = wwin->screen_ptr;
|
|
WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
|
|
*x = rect.pos.x + (rect.size.width - width) / 2;
|
|
*y = rect.pos.y + (rect.size.height - height) / 2;
|
|
}
|
|
}
|
|
|
|
static void updateWindowType(WWindow * wwin)
|
|
{
|
|
Atom type_ret;
|
|
int fmt_ret;
|
|
unsigned long nitems_ret;
|
|
unsigned long bytes_after_ret;
|
|
long *data = 0;
|
|
int layer;
|
|
if (XGetWindowProperty(dpy, wwin->client_win, net_wm_window_type, 0, 1,
|
|
False, XA_ATOM, &type_ret, &fmt_ret, &nitems_ret,
|
|
&bytes_after_ret, (unsigned char **)&data) == Success && data) {
|
|
|
|
int i;
|
|
Atom *type = (Atom *) data;
|
|
for (i = 0; i < nitems_ret; ++i) {
|
|
if (handleWindowType(wwin, type[i], &layer))
|
|
break;
|
|
}
|
|
XFree(data);
|
|
}
|
|
|
|
if (wwin->frame != NULL) {
|
|
ChangeStackingLevel(wwin->frame->core, layer);
|
|
wwin->frame->flags.need_texture_change = 1;
|
|
wWindowConfigureBorders(wwin);
|
|
wFrameWindowPaint(wwin->frame);
|
|
wNETWMUpdateActions(wwin, False);
|
|
}
|
|
}
|
|
|
|
Bool wNETWMCheckClientHints(WWindow * wwin, int *layer, int *workspace)
|
|
{
|
|
Atom type_ret;
|
|
int fmt_ret;
|
|
unsigned long nitems_ret;
|
|
unsigned long bytes_after_ret;
|
|
long *data = 0;
|
|
Bool hasState = False;
|
|
int i;
|
|
|
|
if (XGetWindowProperty(dpy, wwin->client_win, net_wm_desktop, 0, 1, False,
|
|
XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
|
|
&bytes_after_ret, (unsigned char **)&data) == Success && data) {
|
|
|
|
long desktop = *data;
|
|
XFree(data);
|
|
|
|
if (desktop == -1)
|
|
wwin->client_flags.omnipresent = 1;
|
|
else
|
|
*workspace = desktop;
|
|
|
|
hasState = True;
|
|
}
|
|
|
|
if (XGetWindowProperty(dpy, wwin->client_win, net_wm_state, 0, 1, False,
|
|
XA_ATOM, &type_ret, &fmt_ret, &nitems_ret,
|
|
&bytes_after_ret, (unsigned char **)&data) == Success && data) {
|
|
|
|
Atom *state = (Atom *) data;
|
|
for (i = 0; i < nitems_ret; ++i) {
|
|
doStateAtom(wwin, state[i], _NET_WM_STATE_ADD, True);
|
|
}
|
|
XFree(data);
|
|
hasState = True;
|
|
}
|
|
|
|
if (XGetWindowProperty(dpy, wwin->client_win, net_wm_window_type, 0, 1, False,
|
|
XA_ATOM, &type_ret, &fmt_ret, &nitems_ret,
|
|
&bytes_after_ret, (unsigned char **)&data) == Success && data) {
|
|
|
|
Atom *type = (Atom *) data;
|
|
for (i = 0; i < nitems_ret; ++i) {
|
|
if (handleWindowType(wwin, type[i], layer))
|
|
break;
|
|
}
|
|
XFree(data);
|
|
hasState = True;
|
|
}
|
|
|
|
wNETWMUpdateActions(wwin, False);
|
|
updateStrut(wwin, False);
|
|
if (updateStrut(wwin, True)) {
|
|
hasState = True;
|
|
}
|
|
wScreenUpdateUsableArea(wwin->screen_ptr);
|
|
|
|
return hasState;
|
|
}
|
|
|
|
static Bool updateNetIconInfo(WWindow * wwin)
|
|
{
|
|
|
|
Atom type_ret;
|
|
int fmt_ret;
|
|
unsigned long nitems_ret;
|
|
unsigned long bytes_after_ret;
|
|
long *data = 0;
|
|
Bool hasState = False;
|
|
Bool old_state = wwin->flags.net_handle_icon;
|
|
|
|
if (XGetWindowProperty(dpy, wwin->client_win, net_wm_handled_icons, 0, 1, False,
|
|
XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
|
|
&bytes_after_ret, (unsigned char **)&data) == Success && data) {
|
|
long handled = *data;
|
|
wwin->flags.net_handle_icon = (handled != 0);
|
|
XFree(data);
|
|
hasState = True;
|
|
|
|
} else
|
|
wwin->flags.net_handle_icon = False;
|
|
|
|
if (XGetWindowProperty(dpy, wwin->client_win, net_wm_icon_geometry, 0, 4, False,
|
|
XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
|
|
&bytes_after_ret, (unsigned char **)&data) == Success && data) {
|
|
|
|
#ifdef NETWM_PROPER
|
|
if (wwin->flags.net_handle_icon)
|
|
#else
|
|
wwin->flags.net_handle_icon = True;
|
|
#endif
|
|
{
|
|
wwin->icon_x = data[0];
|
|
wwin->icon_y = data[1];
|
|
wwin->icon_w = data[2];
|
|
wwin->icon_h = data[3];
|
|
}
|
|
|
|
XFree(data);
|
|
hasState = True;
|
|
|
|
} else
|
|
wwin->flags.net_handle_icon = False;
|
|
|
|
if (wwin->flags.miniaturized && old_state != wwin->flags.net_handle_icon) {
|
|
if (wwin->flags.net_handle_icon) {
|
|
removeIcon(wwin);
|
|
} else {
|
|
wwin->flags.miniaturized = False;
|
|
wwin->flags.skip_next_animation = True;
|
|
wIconifyWindow(wwin);
|
|
}
|
|
}
|
|
|
|
return hasState;
|
|
}
|
|
|
|
Bool wNETWMCheckInitialClientState(WWindow * wwin)
|
|
{
|
|
Bool hasState = False;
|
|
|
|
#ifdef DEBUG_WMSPEC
|
|
printf("CheckInitialClientState\n");
|
|
#endif
|
|
|
|
wNETWMShowingDesktop(wwin->screen_ptr, False);
|
|
|
|
hasState |= updateNetIconInfo(wwin);
|
|
|
|
updateIconImage(wwin->screen_ptr, wwin);
|
|
|
|
return hasState;
|
|
}
|
|
|
|
static void handleDesktopNames(XClientMessageEvent * event, WScreen * scr)
|
|
{
|
|
unsigned long nitems_ret, bytes_after_ret;
|
|
char *data, *names[32];
|
|
int fmt_ret, i, n;
|
|
Atom type_ret;
|
|
|
|
if (XGetWindowProperty(dpy, scr->root_win, net_desktop_names, 0, 1, False,
|
|
utf8_string, &type_ret, &fmt_ret, &nitems_ret,
|
|
&bytes_after_ret, (unsigned char **)&data) != Success) {
|
|
return;
|
|
}
|
|
|
|
if (data == 0)
|
|
return;
|
|
|
|
if (type_ret != utf8_string || fmt_ret != 8)
|
|
return;
|
|
|
|
n = 0;
|
|
names[n] = data;
|
|
for (i = 0; i < nitems_ret; i++) {
|
|
if (data[i] == 0) {
|
|
n++;
|
|
names[n] = &data[i];
|
|
} else if (*names[n] == 0) {
|
|
names[n] = &data[i];
|
|
wWorkspaceRename(scr, n, names[n]);
|
|
}
|
|
}
|
|
}
|
|
|
|
Bool wNETWMProcessClientMessage(XClientMessageEvent * event)
|
|
{
|
|
WScreen *scr;
|
|
WWindow *wwin;
|
|
Bool done = True;
|
|
|
|
#ifdef DEBUG_WMSPEC
|
|
printf("processClientMessage type %s\n", XGetAtomName(dpy, event->message_type));
|
|
#endif
|
|
|
|
scr = wScreenForWindow(event->window);
|
|
if (scr) {
|
|
/* generic client messages */
|
|
if (event->message_type == net_current_desktop) {
|
|
wWorkspaceChange(scr, event->data.l[0]);
|
|
} else if (event->message_type == net_number_of_desktops) {
|
|
long value;
|
|
|
|
value = event->data.l[0];
|
|
if (value > scr->workspace_count) {
|
|
wWorkspaceMake(scr, value - scr->workspace_count);
|
|
} else if (value < scr->workspace_count) {
|
|
int i;
|
|
Bool rebuild = False;
|
|
|
|
for (i = scr->workspace_count - 1; i >= value; i--) {
|
|
if (!wWorkspaceDelete(scr, i)) {
|
|
rebuild = True;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rebuild) {
|
|
updateWorkspaceCount(scr);
|
|
}
|
|
}
|
|
} else if (event->message_type == net_showing_desktop) {
|
|
wNETWMShowingDesktop(scr, event->data.l[0]);
|
|
#ifdef VIRTUAL_DESKTOP
|
|
} else if (event->message_type == net_desktop_viewport) {
|
|
wWorkspaceSetViewport(scr, scr->current_workspace, event->data.l[0], event->data.l[1]);
|
|
#endif
|
|
} else if (event->message_type == net_desktop_names) {
|
|
handleDesktopNames(event, scr);
|
|
} else {
|
|
done = False;
|
|
}
|
|
|
|
if (done)
|
|
return True;
|
|
}
|
|
|
|
/* window specific client messages */
|
|
|
|
wwin = wWindowFor(event->window);
|
|
if (!wwin)
|
|
return False;
|
|
|
|
if (event->message_type == net_active_window) {
|
|
/*
|
|
* Firefox sends aditional 'net_active_window' signals on startup if
|
|
* multiple tabs are open. That causes unnecessary workspace switching if
|
|
* those signals come from other workspaces. Therefore we also check if
|
|
* we should ignore these spurious focus across workspaces requests (but
|
|
* allow the switching if it comes from a pager).
|
|
*/
|
|
if (wwin->frame->workspace == wwin->screen_ptr->current_workspace
|
|
|| !WFLAGP(wwin, dont_focus_across_wksp) || event->data.l[0] == 2) {
|
|
wNETWMShowingDesktop(scr, False);
|
|
wMakeWindowVisible(wwin);
|
|
}
|
|
} else if (event->message_type == net_close_window) {
|
|
if (!WFLAGP(wwin, no_closable)) {
|
|
if (wwin->protocols.DELETE_WINDOW)
|
|
wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, LastTimestamp);
|
|
}
|
|
} else if (event->message_type == net_wm_state) {
|
|
int maximized = wwin->flags.maximized;
|
|
long set = event->data.l[0];
|
|
|
|
#ifdef DEBUG_WMSPEC
|
|
printf("net_wm_state set %d a1 %s a2 %s\n", set,
|
|
XGetAtomName(dpy, event->data.l[1]), XGetAtomName(dpy, event->data.l[2]));
|
|
#endif
|
|
|
|
doStateAtom(wwin, (Atom) event->data.l[1], set, False);
|
|
if (event->data.l[2])
|
|
doStateAtom(wwin, (Atom) event->data.l[2], set, False);
|
|
|
|
if (wwin->flags.maximized != maximized) {
|
|
if (!wwin->flags.maximized) {
|
|
wwin->flags.maximized = maximized;
|
|
wUnmaximizeWindow(wwin);
|
|
} else {
|
|
wMaximizeWindow(wwin, wwin->flags.maximized);
|
|
}
|
|
}
|
|
updateStateHint(wwin, False, False);
|
|
} else if (event->message_type == net_wm_handled_icons || event->message_type == net_wm_icon_geometry) {
|
|
updateNetIconInfo(wwin);
|
|
} else if (event->message_type == net_wm_desktop) {
|
|
long desktop = event->data.l[0];
|
|
if (desktop == -1) {
|
|
wWindowSetOmnipresent(wwin, True);
|
|
} else {
|
|
if (IS_OMNIPRESENT(wwin))
|
|
wWindowSetOmnipresent(wwin, False);
|
|
wWindowChangeWorkspace(wwin, desktop);
|
|
}
|
|
} else if (event->message_type == net_wm_icon) {
|
|
updateIconImage(scr, wwin);
|
|
} else {
|
|
done = False;
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
Bool wNETWMCheckClientHintChange(WWindow * wwin, XPropertyEvent * event)
|
|
{
|
|
Bool ret = True;
|
|
|
|
if (event->atom == net_wm_strut) {
|
|
updateStrut(wwin, False);
|
|
updateStrut(wwin, True);
|
|
wScreenUpdateUsableArea(wwin->screen_ptr);
|
|
} else if (event->atom == net_wm_handled_icons || event->atom == net_wm_icon_geometry) {
|
|
updateNetIconInfo(wwin);
|
|
} else if (event->atom == net_wm_window_type) {
|
|
updateWindowType(wwin);
|
|
} else if (event->atom == net_wm_name) {
|
|
char *name = wNETWMGetWindowName(wwin->client_win);
|
|
wWindowUpdateName(wwin, name);
|
|
if (name)
|
|
wfree(name);
|
|
} else if (event->atom == net_wm_icon_name) {
|
|
if (wwin->icon) {
|
|
char *name = wNETWMGetIconName(wwin->client_win);
|
|
wIconChangeTitle(wwin->icon, name);
|
|
}
|
|
} else {
|
|
ret = False;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int wNETWMGetPidForWindow(Window window)
|
|
{
|
|
Atom type_ret;
|
|
int fmt_ret;
|
|
unsigned long nitems_ret;
|
|
unsigned long bytes_after_ret;
|
|
long *data = 0;
|
|
int pid;
|
|
|
|
if (XGetWindowProperty(dpy, window, net_wm_pid, 0, 1, False,
|
|
XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
|
|
&bytes_after_ret, (unsigned char **)&data) == Success && data) {
|
|
|
|
pid = *data;
|
|
XFree(data);
|
|
} else {
|
|
pid = 0;
|
|
}
|
|
|
|
return pid;
|
|
}
|
|
|
|
char *wNETWMGetWindowName(Window window)
|
|
{
|
|
char *name;
|
|
char *ret;
|
|
int size;
|
|
|
|
name = (char *)PropGetCheckProperty(window, net_wm_name, utf8_string, 0, 0, &size);
|
|
if (name) {
|
|
ret = wstrndup(name, size);
|
|
XFree(name);
|
|
} else
|
|
ret = NULL;
|
|
return ret;
|
|
}
|
|
|
|
char *wNETWMGetIconName(Window window)
|
|
{
|
|
char *name;
|
|
char *ret;
|
|
int size;
|
|
|
|
name = (char *)PropGetCheckProperty(window, net_wm_icon_name, utf8_string, 0, 0, &size);
|
|
if (name) {
|
|
ret = wstrndup(name, size);
|
|
XFree(name);
|
|
} else
|
|
ret = NULL;
|
|
return ret;
|
|
}
|
|
|
|
static void observer(void *self, WMNotification * notif)
|
|
{
|
|
WWindow *wwin = (WWindow *) WMGetNotificationObject(notif);
|
|
const char *name = WMGetNotificationName(notif);
|
|
void *data = WMGetNotificationClientData(notif);
|
|
NetData *ndata = (NetData *) self;
|
|
|
|
if (strcmp(name, WMNManaged) == 0 && wwin) {
|
|
updateClientList(wwin->screen_ptr);
|
|
updateClientListStacking(wwin->screen_ptr, NULL);
|
|
updateStateHint(wwin, True, False);
|
|
|
|
updateStrut(wwin, False);
|
|
updateStrut(wwin, True);
|
|
wScreenUpdateUsableArea(wwin->screen_ptr);
|
|
} else if (strcmp(name, WMNUnmanaged) == 0 && wwin) {
|
|
updateClientList(wwin->screen_ptr);
|
|
updateClientListStacking(wwin->screen_ptr, wwin);
|
|
updateWorkspaceHint(wwin, False, True);
|
|
updateStateHint(wwin, False, True);
|
|
wNETWMUpdateActions(wwin, True);
|
|
|
|
updateStrut(wwin, False);
|
|
wScreenUpdateUsableArea(wwin->screen_ptr);
|
|
} else if (strcmp(name, WMNResetStacking) == 0 && wwin) {
|
|
updateClientListStacking(wwin->screen_ptr, NULL);
|
|
updateStateHint(wwin, False, False);
|
|
} else if (strcmp(name, WMNChangedStacking) == 0 && wwin) {
|
|
updateClientListStacking(wwin->screen_ptr, NULL);
|
|
updateStateHint(wwin, False, False);
|
|
} else if (strcmp(name, WMNChangedFocus) == 0) {
|
|
updateFocusHint(ndata->scr, wwin);
|
|
} else if (strcmp(name, WMNChangedWorkspace) == 0 && wwin) {
|
|
updateWorkspaceHint(wwin, False, False);
|
|
updateStateHint(wwin, True, False);
|
|
} else if (strcmp(name, WMNChangedState) == 0 && wwin) {
|
|
updateStateHint(wwin, !strcmp(data, "omnipresent"), False);
|
|
}
|
|
}
|
|
|
|
static void wsobserver(void *self, WMNotification * notif)
|
|
{
|
|
WScreen *scr = (WScreen *) WMGetNotificationObject(notif);
|
|
const char *name = WMGetNotificationName(notif);
|
|
|
|
if (strcmp(name, WMNWorkspaceCreated) == 0) {
|
|
updateWorkspaceCount(scr);
|
|
updateWorkspaceNames(scr);
|
|
} else if (strcmp(name, WMNWorkspaceDestroyed) == 0) {
|
|
updateWorkspaceCount(scr);
|
|
updateWorkspaceNames(scr);
|
|
} else if (strcmp(name, WMNWorkspaceChanged) == 0) {
|
|
updateCurrentWorkspace(scr);
|
|
} else if (strcmp(name, WMNWorkspaceNameChanged) == 0) {
|
|
updateWorkspaceNames(scr);
|
|
}
|
|
}
|
|
|
|
#endif /* NETWM_HINTS */
|