Simdock ist nicht perfekt. Ich mag immer noch, dass die Transparenz auf meinem System funktioniert und wie es die Fenster einzeln durchgeht. Aber eines der Probleme ist, dass es gelegentlich abstürzt(e), ohne jegliche Fehlermeldung. Ich habe daher nicht herausgefunden, woran das liegt. Aber ich hatte ein bisschen Code in einer xstuff.cc, den ich weder verstand noch schön fand. Dieser nutzte die Xlib - und dafür gibt es mit xcb eine Alternative, die bei Fehlern etwas seltener das Programm mitreißen sollte.
Warum überhaupt eine X-Library?
Berechtigte Frage. Simdock nutzt eigentlich wxWidgets, eine C++-Multipalttform-Library. Für die eigentliche Fenstererstellung funktioniert das auch ganz gut. Aber ein Dock muss Sachen machen, die andere Programme nicht tun müssen, die näher an der Aufgabe eines Fenstermanagers sind - beispielsweise Fenster anderer Programme aktivieren. Das kann wxWidgets nicht, oder zumindest ist es nicht so dokumentiert, dass ich es finden würde. Die xstuff.cc hatte diese Funktionen (und ein paar Helfer):
void xstuff_raiseWindow
unsigned int xstuff_getWindowPID
bool xstuff_resizeScreen
void xstuff_set_wmspec_strut
bool xstuff_setDock
Vorgehen
Man findet zu xcb viel weniger Dokumentation als ich gedacht habe, insbesondere viel weniger Codebeispiele. Was existiert ist aber wenigstens meist hilfreich. So auch MixingCalls, der Hinweis, dass man bestehenden Xlib-Code einfach mit xcb mischen kann. Genau das machte ich beim Ersetzen der xstuff_setDock
, die _NET_WM_WINDOW_TYPE
auf _NET_WM_WINDOW_TYPE_DOCK
setzen soll. Vorher sah die so aus:
/* Stolen from gnome-panel */
bool
xstuff_setDock (Display * xdisplay, Window winID)
{
if (winID == 0)
return false;
if (!xdisplay)
return false;
Atom a[2] = { None, None };
a[0] = XInternAtom (xdisplay, "_NET_WM_WINDOW_TYPE_DOCK", FALSE);
Atom a2 = XInternAtom (xdisplay, "_NET_WM_WINDOW_TYPE", FALSE);
XChangeProperty (xdisplay,
winID,
a2, XA_ATOM, 32, PropModeReplace, (unsigned char *) a, 1);
return true;
}
Und danach so:
bool
xstuff_setDock (Display * xdisplay, Window winID) {
if (winID == 0 || !xdisplay) {
return false;
}
xcb_connection_t *c = XGetXCBConnection(xdisplay);
xcb_ewmh_connection_t EWMH;
xcb_intern_atom_cookie_t *EWMHCookie = xcb_ewmh_init_atoms(c, &EWMH);
if (! xcb_ewmh_init_atoms_replies(&EWMH, EWMHCookie, NULL)) {
return false;
}
xcb_change_property(c, /* Connection to the X server */
XCB_PROP_MODE_REPLACE, /* Property mode */
winID, /* Window */
EWMH._NET_WM_WINDOW_TYPE, /* Property to change */
XCB_ATOM_ATOM, /* Type of the property */
32, /* Format of the property (8, 16, 32) */
1, /* Length of the data parameter */
&(EWMH._NET_WM_WINDOW_TYPE_DOCK)); /* Data */
return true;
}
Es gibt hier drei Besonderheiten:
- Mit
XGetXCBConnection(xdisplay);
wird Display* von Xlib in xcb als connection benutzt.
- xcb reichte nicht aus, weil das
_NET_WM_WINDOW_TYPE
nicht kennt. Man kann aber XInternAtom von Xlib benutzen. Ich habe stattdessen auf xcb_ewmh zurückgegriffen.
xcb_ewmh_init_atoms_replies
ist von http://stackoverflow.com/a/13319374/2508518
Weitere Vereinfachungen
Im aktuellen Master ist die Funktion gleich verschwunden. Denn statt xcb gibt es noch eine Alternative: libwnck. Diese Lib nutzt simdock sowieso schon, und mit ihr geht das ganze so:
WnckWindow* window = wnck_window_get(winID);
wnck_window_set_window_type(window, WNCK_WINDOW_DOCK);
Weil das ausreichend kurz ist, habe ich mehrere solcher Aufrufe in einer xstuff_setDefaultWindowFlags
kombiniert.
Doch auch für xcb gibt es noch Vereinfachungen, und zwar in xcb_ewmh. Dort sind einige Hilfsfunktionen definiert, mit denen Attribute leichter abgefragt und gesetzt werden können. Statt also mit xcb_get_property - ähnlich aufgebaut wie xcb_change_property - zu arbeiten, kann simdock die PID eines Fensters so erfragen:
unsigned int
xstuff_getWindowPID(Window winID) {
xcb_connection_t* conn = xcb_connect (NULL, NULL);
xcb_ewmh_connection_t ewmh_conn;
xcb_intern_atom_cookie_t* ewmh_cookie = xcb_ewmh_init_atoms(conn, &ewmh_conn);
if(! xcb_ewmh_init_atoms_replies(&ewmh_conn, ewmh_cookie, NULL)) {
return false;
}
uint32_t pid;
xcb_ewmh_get_wm_pid_reply(&ewmh_conn, xcb_ewmh_get_wm_pid(&ewmh_conn, winID), &pid, NULL);
xcb_disconnect(conn);
return pid;
}
Mit ein bisschen besserer Struktur (die connection müsste ja nicht in der Funktion erstellt werden) könnte das fast so simpel sein wie der Wnck-Weg.
Aktueller Stand
So bin ich Funktion für Funktion durch den Code gegangen und habe die Xlib durch xcb oder wnck ersetzt. Hat es was gebracht? Mir zumindest ist simdock bisher nicht mehr abgestürzt, aber das heißt natürlich noch nichts. Es wird sich mit der Zeit zeigen. Aber selbst wenn nun alles gut ist: Ich habe auch ein paar andere Ecken leicht verändert, z.B. die Größenberechnung von Icons und Programm, was auch geholfen haben könnte.
Aber ich glaube, dass schon die Codeersparnis bei weiteren Verbesserungen helfen wird. Und xcb bietet nette Möglichkeiten, Calls zum Xserver zu kombinieren, damit kann man sicher noch einiges verbessern.