ede/ede-panel/Netwm.cpp

565 lines
15 KiB
C++

#include <string.h>
#include <X11/Xproto.h>
#include <FL/Fl.H>
#include <FL/x.H>
#include <FL/Fl_Window.H>
#include <edelib/Debug.h>
#include <edelib/List.h>
#include "Netwm.h"
EDELIB_NS_USING(list)
struct NetwmCallbackData {
NetwmCallback cb;
void *data;
};
typedef list<NetwmCallbackData> CbList;
typedef list<NetwmCallbackData>::iterator CbListIt;
static CbList callback_list;
static bool input_selected = false;
static Atom _XA_NET_WORKAREA;
static Atom _XA_NET_WM_WINDOW_TYPE;
static Atom _XA_NET_WM_WINDOW_TYPE_DOCK;
static Atom _XA_NET_WM_WINDOW_TYPE_DESKTOP;
static Atom _XA_NET_WM_WINDOW_TYPE_SPLASH;
static Atom _XA_NET_WM_STRUT;
static Atom _XA_NET_NUMBER_OF_DESKTOPS;
static Atom _XA_NET_CURRENT_DESKTOP;
static Atom _XA_NET_DESKTOP_NAMES;
static Atom _XA_NET_CLIENT_LIST;
static Atom _XA_NET_WM_DESKTOP;
static Atom _XA_NET_WM_NAME;
static Atom _XA_NET_WM_VISIBLE_NAME;
static Atom _XA_NET_ACTIVE_WINDOW;
static Atom _XA_NET_CLOSE_WINDOW;
static Atom _XA_WM_STATE;
static Atom _XA_NET_WM_STATE;
static Atom _XA_NET_WM_STATE_MAXIMIZED_HORZ;
static Atom _XA_NET_WM_STATE_MAXIMIZED_VERT;
static Atom _XA_EDE_WM_ACTION;
static Atom _XA_EDE_WM_RESTORE_SIZE;
/* this macro is set in xlib when X-es provides UTF-8 extension (since XFree86 4.0.2) */
#if X_HAVE_UTF8_STRING
static Atom _XA_UTF8_STRING;
#else
# define _XA_UTF8_STRING XA_STRING
#endif
static short atoms_inited = 0;
static void init_atoms_once(void) {
if(atoms_inited)
return;
_XA_NET_WORKAREA = XInternAtom(fl_display, "_NET_WORKAREA", False);
_XA_NET_WM_WINDOW_TYPE = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE", False);
_XA_NET_WM_WINDOW_TYPE_DOCK = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_DOCK", False);
_XA_NET_WM_WINDOW_TYPE_SPLASH = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_SPLASH", False);
_XA_NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
_XA_NET_WM_STRUT = XInternAtom(fl_display, "_NET_WM_STRUT", False);
_XA_NET_NUMBER_OF_DESKTOPS = XInternAtom(fl_display, "_NET_NUMBER_OF_DESKTOPS", False);
_XA_NET_CURRENT_DESKTOP = XInternAtom(fl_display, "_NET_CURRENT_DESKTOP", False);
_XA_NET_DESKTOP_NAMES = XInternAtom(fl_display, "_NET_DESKTOP_NAMES", False);
_XA_NET_CLIENT_LIST = XInternAtom(fl_display, "_NET_CLIENT_LIST", False);
_XA_NET_WM_DESKTOP = XInternAtom(fl_display, "_NET_WM_DESKTOP", False);
_XA_NET_WM_NAME = XInternAtom(fl_display, "_NET_WM_NAME", False);
_XA_NET_WM_VISIBLE_NAME = XInternAtom(fl_display, "_NET_WM_VISIBLE_NAME", False);
_XA_NET_ACTIVE_WINDOW = XInternAtom(fl_display, "_NET_ACTIVE_WINDOW", False);
_XA_NET_CLOSE_WINDOW = XInternAtom(fl_display, "_NET_CLOSE_WINDOW", False);
_XA_WM_STATE = XInternAtom(fl_display, "WM_STATE", False);
_XA_NET_WM_STATE = XInternAtom(fl_display, "_NET_WM_STATE", False);
_XA_NET_WM_STATE_MAXIMIZED_HORZ = XInternAtom(fl_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
_XA_NET_WM_STATE_MAXIMIZED_VERT = XInternAtom(fl_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
_XA_EDE_WM_ACTION = XInternAtom(fl_display, "_EDE_WM_ACTION", False);
_XA_EDE_WM_RESTORE_SIZE = XInternAtom(fl_display, "_EDE_WM_RESTORE_SIZE", False);
#ifdef X_HAVE_UTF8_STRING
_XA_UTF8_STRING = XInternAtom(fl_display, "UTF8_STRING", False);
#endif
atoms_inited = 1;
}
static int xevent_handler(int e) {
if(fl_xevent->type == PropertyNotify) {
int action = -1;
E_DEBUG("==> %s\n", XGetAtomName(fl_display, fl_xevent->xproperty.atom));
if(fl_xevent->xproperty.atom == _XA_NET_NUMBER_OF_DESKTOPS)
action = NETWM_CHANGED_WORKSPACE_COUNT;
else if(fl_xevent->xproperty.atom == _XA_NET_DESKTOP_NAMES)
action = NETWM_CHANGED_WORKSPACE_NAMES;
else if(fl_xevent->xproperty.atom == _XA_NET_CURRENT_DESKTOP)
action = NETWM_CHANGED_CURRENT_WORKSPACE;
else if(fl_xevent->xproperty.atom == _XA_NET_WORKAREA)
action = NETWM_CHANGED_CURRENT_WORKAREA;
else if(fl_xevent->xproperty.atom == _XA_NET_ACTIVE_WINDOW)
action = NETWM_CHANGED_ACTIVE_WINDOW;
else if(fl_xevent->xproperty.atom == _XA_NET_WM_NAME || fl_xevent->xproperty.atom == XA_WM_NAME)
action = NETWM_CHANGED_WINDOW_NAME;
else if(fl_xevent->xproperty.atom == _XA_NET_WM_VISIBLE_NAME)
action = NETWM_CHANGED_WINDOW_VISIBLE_NAME;
else if(fl_xevent->xproperty.atom == _XA_NET_WM_DESKTOP)
action = NETWM_CHANGED_WINDOW_DESKTOP;
else if(fl_xevent->xproperty.atom == _XA_NET_CLIENT_LIST)
action = NETWM_CHANGED_WINDOW_LIST;
else
action = -1;
if(action >= 0 && !callback_list.empty()) {
Window xid = fl_xevent->xproperty.window;
/* TODO: locking here */
CbListIt it = callback_list.begin(), it_end = callback_list.end();
for(; it != it_end; ++it) {
/* execute callback */
(*it).cb(action, xid, (*it).data);
}
}
}
return 0;
}
void netwm_callback_add(NetwmCallback cb, void *data) {
E_RETURN_IF_FAIL(cb != NULL);
fl_open_display();
init_atoms_once();
/* to catch _XA_NET_CURRENT_DESKTOP and such events */
if(!input_selected) {
XSelectInput(fl_display, RootWindow(fl_display, fl_screen), PropertyChangeMask | StructureNotifyMask);
input_selected = true;
}
NetwmCallbackData cb_data;
cb_data.cb = cb;
cb_data.data = data;
callback_list.push_back(cb_data);
Fl::remove_handler(xevent_handler);
Fl::add_handler(xevent_handler);
}
void netwm_callback_remove(NetwmCallback cb) {
if(callback_list.empty())
return;
CbListIt it = callback_list.begin(), it_end = callback_list.end();
while(it != it_end) {
if(cb == (*it).cb) {
it = callback_list.erase(it);
} else {
/* continue, in case the same callback is put multiple times */
++it;
}
}
}
bool netwm_get_workarea(int& x, int& y, int& w, int &h) {
init_atoms_once();
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop;
x = y = w = h = 0;
int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
_XA_NET_WORKAREA, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra, (unsigned char**)&prop);
if(status != Success)
return false;
CARD32* val = (CARD32*)prop;
if(val) {
x = val[0];
y = val[1];
w = val[2];
h = val[3];
XFree((char*)val);
return true;
}
return false;
}
void netwm_make_me_dock(Fl_Window *win) {
init_atoms_once();
XChangeProperty(fl_display, fl_xid(win), _XA_NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace,
(unsigned char*)&_XA_NET_WM_WINDOW_TYPE_DOCK, sizeof(Atom));
}
void netwm_set_window_strut(Fl_Window *win, int left, int right, int top, int bottom) {
init_atoms_once();
CARD32 strut[4] = { left, right, top, bottom };
XChangeProperty(fl_display, fl_xid(win), _XA_NET_WM_STRUT, XA_CARDINAL, 32,
PropModeReplace, (unsigned char*)&strut, sizeof(CARD32) * 4);
}
int netwm_get_workspace_count(void) {
init_atoms_once();
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop;
int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
_XA_NET_NUMBER_OF_DESKTOPS, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra,
(unsigned char**)&prop);
if(status != Success || !prop)
return -1;
int ns = int(*(long*)prop);
XFree(prop);
return ns;
}
/* change current workspace */
void netwm_change_workspace(int n) {
init_atoms_once();
XEvent xev;
Window root_win = RootWindow(fl_display, fl_screen);
memset(&xev, 0, sizeof(xev));
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.window = root_win;
xev.xclient.display = fl_display;
xev.xclient.message_type = _XA_NET_CURRENT_DESKTOP;
xev.xclient.format = 32;
xev.xclient.data.l[0] = (long)n;
xev.xclient.data.l[1] = CurrentTime;
XSendEvent (fl_display, root_win, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
XSync(fl_display, True);
}
int netwm_get_current_workspace(void) {
init_atoms_once();
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop;
int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
_XA_NET_CURRENT_DESKTOP, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra,
(unsigned char**)&prop);
if(status != Success || !prop)
return -1;
int ns = int(*(long*)prop);
XFree(prop);
return ns;
}
int netwm_get_workspace_names(char**& names) {
init_atoms_once();
/* FIXME: add _NET_SUPPORTING_WM_CHECK and _NET_SUPPORTED ??? */
XTextProperty wnames;
XGetTextProperty(fl_display, RootWindow(fl_display, fl_screen), &wnames, _XA_NET_DESKTOP_NAMES);
/* if wm does not understainds _NET_DESKTOP_NAMES this is not set */
if(!wnames.nitems || !wnames.value)
return 0;
int nsz;
/*
* FIXME: Here should as alternative Xutf8TextPropertyToTextList since
* many wm's set UTF8_STRING property. Below is XA_STRING and for UTF8_STRING
* will fail.
*/
if(!XTextPropertyToStringList(&wnames, &names, &nsz)) {
XFree(wnames.value);
return 0;
}
XFree(wnames.value);
return nsz;
}
int netwm_get_mapped_windows(Window **windows) {
init_atoms_once();
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop = 0;
int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
_XA_NET_CLIENT_LIST, 0L, 0x7fffffff, False, XA_WINDOW, &real, &format, &n, &extra,
(unsigned char**)&prop);
if(status != Success || !prop)
return -1;
*windows = (Window*)prop;
return n;
}
int netwm_get_window_workspace(Window win) {
E_RETURN_VAL_IF_FAIL(win >= 0, -1);
init_atoms_once();
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop = 0;
int status = XGetWindowProperty(fl_display, win,
_XA_NET_WM_DESKTOP, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra,
(unsigned char**)&prop);
if(status != Success || !prop)
return -1;
//int ns = int(*(long*)prop);
unsigned long desk = (unsigned long)(*(long*)prop);
XFree(prop);
/* sticky */
if(desk == 0xFFFFFFFF || desk == 0xFFFFFFFE)
return -1;
return desk;
}
int netwm_manageable_window(Window win) {
init_atoms_once();
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop = 0;
int status = XGetWindowProperty(fl_display, win,
_XA_NET_WM_WINDOW_TYPE, 0L, sizeof(Atom), False, XA_ATOM, &real, &format, &n, &extra,
(unsigned char**)&prop);
if(status != Success || !prop)
return -1;
Atom type = *(Atom*)prop;
XFree(prop);
if(type == _XA_NET_WM_WINDOW_TYPE_DESKTOP ||
type == _XA_NET_WM_WINDOW_TYPE_SPLASH ||
type == _XA_NET_WM_WINDOW_TYPE_DOCK)
{
return 0;
}
return 1;
}
char *netwm_get_window_title(Window win) {
init_atoms_once();
XTextProperty xtp;
char *title = NULL, *ret = NULL;
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop = 0;
int status = XGetWindowProperty(fl_display, win,
_XA_NET_WM_NAME, 0L, 0x7fffffff, False, _XA_UTF8_STRING , &real, &format, &n, &extra,
(unsigned char**)&prop);
if(status == Success && prop) {
title = (char *)prop;
ret = strdup(title);
XFree(prop);
} else {
/* try WM_NAME */
if(!XGetWMName(fl_display, win, &xtp))
return NULL;
if(xtp.encoding == XA_STRING) {
ret = strdup((const char*)xtp.value);
} else {
#if X_HAVE_UTF8_STRING
char **lst;
int lst_sz;
status = Xutf8TextPropertyToTextList(fl_display, &xtp, &lst, &lst_sz);
if(status == Success && lst_sz > 0) {
ret = strdup((const char*)*lst);
XFreeStringList(lst);
} else {
ret = strdup((const char*)xtp.value);
}
#else
ret = strdup((const char*)tp.value);
#endif
}
XFree(xtp.value);
}
return ret;
}
Window netwm_get_active_window(void) {
init_atoms_once();
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop = 0;
int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
_XA_NET_ACTIVE_WINDOW, 0L, sizeof(Atom), False, XA_WINDOW, &real, &format, &n, &extra,
(unsigned char**)&prop);
if(status != Success || !prop)
return -1;
int ret = int(*(long*)prop);
XFree(prop);
return (Window)ret;
}
void netwm_set_active_window(Window win) {
init_atoms_once();
XEvent xev;
memset(&xev, 0, sizeof(xev));
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.window = win;
xev.xclient.display = fl_display;
xev.xclient.message_type = _XA_NET_ACTIVE_WINDOW;
xev.xclient.format = 32;
xev.xclient.data.l[0] = (long)win;
xev.xclient.data.l[1] = CurrentTime;
XSendEvent (fl_display, RootWindow(fl_display, fl_screen), False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
XSync(fl_display, True);
}
void netwm_maximize_window(Window win) {
init_atoms_once();
XEvent xev;
memset(&xev, 0, sizeof(xev));
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.window = win;
xev.xclient.display = fl_display;
xev.xclient.message_type = _XA_NET_WM_STATE;
xev.xclient.format = 32;
xev.xclient.data.l[0] = 0;
xev.xclient.data.l[1] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
xev.xclient.data.l[2] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
XSendEvent (fl_display, RootWindow(fl_display, fl_screen), False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
XSync(fl_display, True);
}
void netwm_close_window(Window win) {
init_atoms_once();
XEvent xev;
memset(&xev, 0, sizeof(xev));
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.window = win;
xev.xclient.display = fl_display;
xev.xclient.message_type = _XA_NET_CLOSE_WINDOW;
xev.xclient.format = 32;
xev.xclient.data.l[0] = (long)win;
xev.xclient.data.l[1] = CurrentTime;
XSendEvent (fl_display, RootWindow(fl_display, fl_screen), False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
XSync(fl_display, True);
}
void wm_ede_restore_window(Window win) {
init_atoms_once();
XEvent xev;
memset(&xev, 0, sizeof(xev));
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.window = win;
xev.xclient.display = fl_display;
xev.xclient.message_type = _XA_EDE_WM_ACTION;
xev.xclient.format = 32;
xev.xclient.data.l[0] = _XA_EDE_WM_RESTORE_SIZE;
xev.xclient.data.l[1] = CurrentTime;
XSendEvent (fl_display, RootWindow(fl_display, fl_screen), False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
XSync(fl_display, True);
}
WmStateValue wm_get_window_state(Window win) {
init_atoms_once();
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop = 0;
int status = XGetWindowProperty(fl_display, win,
_XA_WM_STATE, 0L, sizeof(Atom), False, _XA_WM_STATE, &real, &format, &n, &extra,
(unsigned char**)&prop);
if(status != Success || !prop)
return WM_STATE_NONE;
WmStateValue ret = WmStateValue(*(long*)prop);
XFree(prop);
return ret;
}
void wm_set_window_state(Window win, WmStateValue state) {
E_RETURN_IF_FAIL((int)state > 0);
if(state == WM_STATE_WITHDRAW) {
XWithdrawWindow(fl_display, win, fl_screen);
XSync(fl_display, True);
} else if(state == WM_STATE_ICONIC) {
XIconifyWindow(fl_display, win, fl_screen);
XSync(fl_display, True);
}
/* nothing for WM_STATE_NORMAL */
}