]> git.sur5r.net Git - i3/i3/blob - docs/GPN-2009-06-27/i3.tex
Merge pull request #3329 from lasers/next
[i3/i3] / docs / GPN-2009-06-27 / i3.tex
1 %
2 % © 2009 Michael Stapelberg
3 %
4 % 2009-06-24
5 %
6 \documentclass[mode=print,paper=screen,style=jefka]{powerdot}
7 \usepackage[utf8]{inputenc}
8 \usepackage{graphicx}
9 \usepackage{float}
10 \usepackage{ngerman}
11 \usepackage{url}
12 \usepackage{listings}
13 \newcommand{\bs}{\textbackslash}
14 \pdsetup{palette=white}
15 \definecolor{darkblue}{rgb}{0,0,.6}
16 \definecolor{darkred}{rgb}{.6,0,0}
17 \definecolor{darkgreen}{rgb}{0,.6,0}
18 \definecolor{darkgray}{gray}{.3}
19 \definecolor{lightblue}{rgb}{0.97,0.99,1}
20
21 \lstloadlanguages{C}
22 \lstdefinestyle{colors}{keywordstyle={\bf\color{darkblue}}, commentstyle={\em\color{magenta}}, stringstyle={\color{darkred}},%
23                         emphstyle={\color{darkgray}}}
24 \lstnewenvironment{code}{%
25         \lstset{frame=single, basicstyle=\footnotesize\ttfamily, language=C, showstringspaces=false,%
26                 style=colors, numbers=left, morekeywords={xcb_get_window_attributes_cookie_t, xcb_map_request_event_t,%
27                 xcb_connection_t, xcb_get_window_attributes_reply_t, window_attributes_t, xcb_intern_atom_cookie_t,%
28                 xcb_intern_atom_reply_t, xcb_atom_t, uint32_t, uint16_t, foreach, UINT_MAX, NULL},%
29                 moreemph={xcb_get_window_attributes_reply, xcb_get_window_attributes_unchecked, manage_window,%
30                 add_ignore_event, xcb_intern_atom, xcb_intern_atom_reply, fprintf, printf, free, load_configuration,%
31                 XInternAtom, exit, strlen, xcb_change_window_attributes, xcb_event_wait_for_event_loop,%
32                 xcb_event_set_key_press_handler, xcb_property_set_handler}}
33 }{}
34
35 \newcommand{\isrc}[1]{\begin{center} \footnotesize\ttfamily Siehe auch: #1 \end{center}}
36
37 \title{Hacking your own window manager}
38 \author{sECuRE auf der GPN 8\\
39 ~\\
40 powered by \LaTeX, of course}
41 \begin{document}
42 \maketitle
43
44 \begin{slide}{Dieser Vortrag}
45 \begin{list}{$\bullet$}{\itemsep=.5em}
46         \item Geschichte/Einführung in Window Manager
47         \item Merkmale von i3
48         \item Window Manager und X11
49         %
50         % zuerst: wie funktioniert ein client?
51         %
52         % WM ist nur ein weiterer Client
53         % Keine Rechteverwaltung, prinzipiell darf jeder Fenster schubsen
54         % Clients können Events abfangen, der WM macht das halt für das root-fenster
55         \item Arbeitsumgebung
56         \item XCB
57         \item Setup
58         \item Reparenting (Window Decorations)
59         %\item fake\_configure\_notify
60         %\item Colorpixel
61         %\item UTF-8
62         % irgendwo da erwähnen: fenster in eine hashtable aufnehmen
63
64         \item Events
65         % (die kriegt man natürlich nur wenn man redirectmask gesetzt hat:)
66         % MapRequest
67         % ConfigureRequest
68         \item Hints (Titel, Klassen, Größen, …)
69         % Atoms
70         % NetWM
71         % - NET_WM_WINDOW_TYPE
72         % - NET_WM_NAME
73         %   - in kombination mit NET_SUPPORTING_WM_CHECK auf dem rootfenster
74         % - NET_WM_STRUT_PARTIAL
75         % ICCCM
76         % - Normal hints / size hints (warum zwei namen?)
77         %   - Aspect ratio, wichtig z.B. für mplayer
78         %   - min/max size, interessant primär für floating
79         % - WM_NAME
80         % - WM_TRANSIENT_FOR
81         % - WM_CLASS
82         \item Gotchas
83         % flush()
84         % WM_STATE_NORMAL und drag&drop in gtk-apps
85         \item Zusammenfassung
86         % TODO
87 \end{list}
88 \end{slide}
89
90 \begin{slide}{Geschichte/Einführung}
91 \begin{list}{$\bullet$}{\itemsep=1em}
92         \item<1-> „All window managers suck, this one just sucks less”?
93         \item<2-> Desktop environment vs. window manager (GNOME, KDE, Xfce, …)
94         \item<3-> Stacking (e17, fluxbox, IceWM, fvwm, …) vs Tiling (dwm, wmii, xmonad, …)
95         \item<4-> dwm, awesome, xmonad, …: statisches Layout
96         % gedanke: man braucht sich nicht mal mehr um das layout kümmern
97         \item<5-> wmii, ion: dynamisches layout
98         \item<6-> Probleme an ion: tuomov (Lizenz, Kommunikation), Config, Look and feel, Code
99         \item<7-> Probleme an wmii: Xinerama-support, Xlib, undokumentierter Code, nur Spalten, keine Reihen, Kleinigkeiten (titellose Fenster)
100 \end{list}
101 \end{slide}
102
103 \begin{slide}{Merkmale von i3}
104 \begin{list}{$\bullet$}{\itemsep=1em}
105         \item<1-> gut lesbarer, dokumentierter Code. Dokumentation.
106         \item<2-> XCB anstelle von Xlib
107         \item<3-> Xinerama done right™
108         \item<4-> Spalten und Zeilen, Tabelle als Basis
109         \item<5-> command-mode, wie in vim
110         \item<6-> UTF-8 clean
111         \item<7-> kein Antialiasing, schlank und schnell bleiben
112 \end{list}
113 \end{slide}
114
115 \begin{slide}{Typische Kommunikation mit X}
116 \begin{list}{$\bullet$}{\itemsep=1em}
117         \item<1-> Verbindung aufbauen
118         \item<2-> Requests über die Leitung schicken (Fenster erzeugen)
119         \begin{list}{$\bullet$}{\itemsep=1em}
120                 \item Cookie für jeden Request
121                 \item Antwort für spezifisches Cookie abholen
122                 \item $\Rightarrow$ Asynchronität nutzbar
123         \end{list}
124         \item<3-> Eventloop starten, reagieren (Fenster zeichnen, Eingaben, …)
125 \end{list}
126 \end{slide}
127
128 \begin{slide}{Was genau macht ein WM?}
129 \begin{list}{$\bullet$}{\itemsep=1em}
130         \item<1-> Events umlenken
131         \item<2-> Neue Fenster anzeigen/positionieren (MapRequest)
132         \item<3-> Titelleisten malen (reparenting)
133         \item<4-> Den Fokus verwalten
134         \item<5-> Mit Hints umgehen (Fenstertitel, Fullscreen, Dock, …)
135         \item<6-> Auf Benutzereingaben reagieren
136 \end{list}
137 \end{slide}
138
139
140 \begin{slide}[method=direct]{Window Manager und X11 (1)}
141 \includegraphics[width=1\textwidth]{xserver_konzept.eps}
142 \end{slide}
143
144 \begin{slide}{Window Manager und X11 (2)}
145 \begin{list}{$\bullet$}{\itemsep=1em}
146         \item<1-> Keine Rechteaufteilung, prinzipiell kann jeder Fenster managen
147         \item<2-> Window Manager verantwortlich für alle Kinder das Root-Fensters
148         \item<3-> RedirectMask, lässt sich Events des Root-Fensters schicken
149         \item<4-> Setzt hints auf dem Root-Fenster
150 \end{list}
151 \end{slide}
152
153 \begin{slide}{Arbeitsumgebung}
154 \begin{list}{$\bullet$}{\itemsep=1em}
155         \item X sinnvoll beim Entwickeln $\Rightarrow$ anderen Computer verwenden oder Xephyr
156         \item xtrace dazwischenschalten (sowohl zwischen WM und X11 als auch zwischen Clients und X11 sinnvoll)\\
157 \texttt{DISPLAY=:1 xtrace -o /tmp/xtrace.log -n :9}
158         \item \texttt{xprop} zeigt Hints an, \texttt{xwininfo} gibt Struktur aus
159         \item als ersten Client ein Terminal starten $\Rightarrow$ wenn der WM crashed lebt
160         die X-Session noch\\
161 \texttt{DISPLAY=:1 urxvt \&}
162         \item Debugger, strace, logfiles, core-dumps aktivieren\\
163         (Siehe auch \url{http://i3wm.org/docs/debugging.html})
164 \end{list}
165 \end{slide}
166
167 \begin{slide}{XCB}
168 \begin{list}{$\bullet$}{\itemsep=1em}
169         \item \url{http://xcb.freedesktop.org/}
170         \item<1-> „X-protocol C-language Binding”
171         \item<2-> Klein, wartbar (aus einer Protokollbeschreibung auto-generiert)
172         \item<3-> Sinnvoll benannte Funktionsnamen und Datentypen
173         \item<4-> Nutzt die Asynchronität von X aus
174         \item<5-> Allerdings: Sehr spärlich dokumentiert, man muss mit Xlib-Doku arbeiten
175         \item<6-> xcb-util: XCB noch mal ein bisschen gekapselt, nützliche Funktionen abstrahiert
176 \end{list}
177 \end{slide}
178
179 \begin{slide}[method=direct]{Xlib-Beispielcode}
180 \begin{code}
181   char *names[10] = {"_NET_SUPPORTED", "_NET_WM_STATE",
182   "_NET_WM_STATE_FULLSCREEN", "_NET_WM_NAME" /* ... */};
183   Atom atoms[10];
184
185   /* Get atoms */
186   for (int i = 0; i < 10; i++) {
187     atoms[i] = XInternAtom(display, names[i], 0);
188   }
189 \end{code}
190 \end{slide}
191
192 \begin{slide}[method=direct]{XCB-Beispielcode}
193 \begin{code}
194 char *names[10] = {"_NET_SUPPORTED", "_NET_WM_STATE",
195   "_NET_WM_STATE_FULLSCREEN", "_NET_WM_NAME" /* ... */};
196 xcb_intern_atom_cookie_t cookies[10];
197
198 /* Place requests for atoms as soon as possible */
199 for (int c = 0; c < 10; c++)
200   cookies[c] = xcb_intern_atom(connection, 0,
201                                strlen(names[c]), names[c]);
202
203 /* Do other stuff here */
204 load_configuration();
205
206 /* Get atoms */
207 for (int c = 0; c < 10; c++) {
208   xcb_intern_atom_reply_t *reply =
209     xcb_intern_atom_reply(connection, cookies[c], NULL);
210   if (!reply) {
211     fprintf(stderr, "Could not get atom\n");
212     exit(-1);
213   }
214   printf("atom has ID %d\n", reply->atom);
215   free(reply);
216 }
217 \end{code}
218 \end{slide}
219
220 \begin{slide}[method=direct]{Setup}
221 \begin{code}
222 get_atoms();
223
224 xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL);
225 xcb_property_set_handler(&prophs, WM_TRANSIENT_FOR, UINT_MAX,
226                          handle_transient_for, NULL);
227
228 xcb_grab_key(conn, 0, root, modifier, keycode,
229              XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
230 xcb_grab_key(conn, 0, root, modifier | xcb_numlock_mask, keycode,
231              XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
232
233 uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
234                       XCB_EVENT_MASK_STRUCTURE_NOTIFY |
235                       XCB_EVENT_MASK_PROPERTY_CHANGE |
236                       XCB_EVENT_MASK_ENTER_WINDOW };
237 xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, values);
238
239 manage_existing_windows();
240
241 xcb_event_wait_for_event_loop(&evenths);
242 \end{code}
243
244 \isrc{i3/src/mainx.c:370ff}
245 \end{slide}
246
247 \begin{slide}[method=direct]{Reparenting}
248 \includegraphics[width=1\textwidth]{reparenting.eps}
249 \begin{enumerate}
250         \item (App) Fenster wird konfiguriert (Position, Größe, …)
251         \item (App) MapRequest
252         \item (WM) Window Manager erstellt eigenes Fenster
253         \item (WM) Reparent = neues Fenster kriegt statt root das WM-Fenster als parent
254         \item (WM) Mappen des neuen Fensters
255 \end{enumerate}
256 \end{slide}
257
258 \begin{slide}[method=direct]{fake\_configure\_notify}
259 \begin{list}{$\bullet$}{\itemsep=.5em}
260         \item (Alte) Reparented clients kriegen nichts mit, denken relativ zum root-Fenster
261         \item $\Rightarrow$ Window Manager tut so, als würde das Fenster neu konfiguriert, sendet den Event mit absoluten statt relativen Koordinaten
262         \item Sieht man sehr gut an \texttt{xfontsel} und anderen Anwendungen, die Xaw (X Athena widget set) verwenden
263 \end{list}
264 \begin{code}
265         xcb_configure_notify_event_t generated_event;
266         generated_event.window = window;
267         generated_event.response_type = XCB_CONFIGURE_NOTIFY;
268         generated_event.x = r.x;
269         /* ... */
270         generated_event.override_redirect = false;
271         xcb_send_event(conn, false, window,
272                        XCB_EVENT_MASK_STRUCTURE_NOTIFY,
273                        (char*)&generated_event);
274 \end{code}
275 \isrc{i3/src/xcb.c:193ff}
276 \end{slide}
277
278
279 \begin{slide}[method=direct]{Events: button\_press}
280 \begin{list}{$\bullet$}{\itemsep=.5em}
281         \item Aktiv grabben, die Anwendung soll keinen Klick bekommen, wenn der Nutzer das Fenster verschiebt
282 \end{list}
283 \begin{code}
284 int handle_button_press(void *ignored, xcb_connection_t *conn,
285                         xcb_button_press_event_t *event) {
286         /* ... */
287         if ((event->state & XCB_MOD_MASK_1) != 0)
288                 floating_drag_window(conn, client, event);
289
290         /* ... */
291         if (event->detail == XCB_BUTTON_INDEX_4 ||
292             event->detail == XCB_BUTTON_INDEX_5) {
293                 LOG("User scrolled\n");
294                 return 1;
295         }
296         /* if unhandled, forward the click to the application */
297         xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
298         return 1;
299 }
300 \end{code}
301 \isrc{i3/src/handlers.c:148ff}
302 \end{slide}
303
304
305 \begin{slide}[method=direct]{Events: enter\_notify}
306 \begin{list}{$\bullet$}{\itemsep=.5em}
307         \item Der Mauszeiger ist über dem Fenster gelandet
308         \item Auch unabsichtlich: Wenn das Fenster unter den Mauszeiger konfiguriert wird
309         \item $\Rightarrow$ Blacklist an Events, die man ignorieren muss
310 \end{list}
311
312 \begin{code}
313 int handle_enter_notify(void *ignored, xcb_connection_t *conn,
314                         xcb_enter_notify_event_t *event) {
315         if (event_is_ignored(event->sequence))
316                 return 1;
317
318         Client *client = table_get(&by_parent, event->event);
319         if (client == NULL) {
320                 return 1; /* user moved cursor to another screen */
321         }
322
323         set_focus(conn, client, false);
324         return 1;
325 }
326 \end{code}
327 \isrc{i3/src/handlers.c:148ff}
328 \end{slide}
329
330
331 \begin{slide}[method=direct]{Events: key\_press }
332 \begin{list}{$\bullet$}{\itemsep=.5em}
333         \item Aktives key grabbing: WM entscheidet, ob Tastendruck weitergeht, also bei der Anwendung ankommt (kann abfangen)
334         \item Passives key grabbing: WM kriegt einen event
335 \end{list}
336
337 \begin{code}
338 uint16_t state_filtered =
339          event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK);
340 state_filtered &= 0xFF; /* filter mouse buttons */
341 foreach (binding) {
342         if (binding->keycode == event->detail &&
343             binding->mods == state_filtered) {
344                 /* do fancy stuff here */
345                 break;
346         }
347 }
348 \end{code}
349 \isrc{i3/src/handlers.c:100ff}
350 \end{slide}
351
352 \begin{slide}[method=direct]{Events: key\_press (2), Mode\_switch }
353 \begin{list}{$\bullet$}{\itemsep=.25em}
354         \item \texttt{event->state} enthält nie das Mode\_switch-Bit, Bug in X
355         \item XKB hilft, den korrekten state zu ermitteln
356         \item $\Rightarrow$ Mode\_switch nicht als modifier in \texttt{xcb\_grab\_key} verwendbar
357         \item $\Rightarrow$ wir grabben alle keys aktiv (!) und filtern selbst nach Mode\_switch
358 \end{list}
359
360 \begin{code}
361 /* ... state_filtered is already cleaned */
362 XkbStateRec state;
363 if (XkbGetState(xkbdpy, XkbUseCoreKbd, &state) == Success &&
364     (state.group+1) == 2)
365         state_filtered |= BIND_MODE_SWITCH;
366 foreach (binding)
367         if (binding->keycode == event->detail &&
368             binding->mods == state_filtered) {
369                 xcb_allow_events(conn, SyncKeyboard, event->time);
370                 return; /* after doing actual stuff, of course */
371         }
372 xcb_allow_events(conn, ReplayKeyboard, event->time);
373 \end{code}
374 \isrc{i3/src/handlers.c:100ff}
375 \end{slide}
376
377
378 \begin{slide}[method=direct]{Umlaute und Sonderzeichen}
379 \includegraphics[width=.5\textwidth]{xft.eps}
380 \begin{list}{$\bullet$}{\itemsep=.1em}
381         \item Verschiedene APIs fürs Rendern von Text: X Core Fonts und xft
382         \item xft = X FreeType, antialiased fonts, Pango, GTK
383         \item Problem mit X Core Fonts: keine Sonderzeichen
384         \item …oder? \texttt{misc-fixed-*-iso10646}, also X Core Fonts mit Universal Character Set (= Unicode-Zeichen). Nicht 100\% vollständig
385         \item urxvt: benutzt beide APIs, pro Glyph unterschiedlich
386         \item Trend geht leider zu fontconfig/xft :-(
387 \end{list}
388 \end{slide}
389
390 \begin{slide}[method=direct]{Umlaute und Sonderzeichen (2)}
391 \begin{list}{$\bullet$}{\itemsep=.5em}
392         \item X hat eigenes Encoding: Compound Text
393         \item Früher ICCCM (Compound text, z.B. Atom WM\_NAME)\\
394         ICCCM = Inter-Client Communication Conventions Manual
395         \item heute EWMH (UTF-8, z.B. Atom \_NET\_WM\_NAME)\\
396         EWMH = Extended Window Manager Hints (= NetWM)
397         \item XImageText16 (bzw xcb\_image\_text\_16) erwartet UCS-2\\
398         $\Rightarrow$ \texttt{iconv\_open(UCS2\_BE, UTF-8)}
399 \end{list}
400 \isrc{i3/src/util.c:191ff, i3/src/handlers.c:663ff}
401 \end{slide}
402
403
404 \begin{slide}[method=direct]{Colorpixel}
405 \begin{list}{$\bullet$}{\itemsep=.5em}
406         \item Heutzutage: TrueColor. Früher: 8 bit o.ä.
407         \item Colormaps: Geben an welche Farben die Hardware kann
408         \item Colorpixel: Ein Wert aus der Colormap, der der gewünschten Farbe am nähesten kommt
409         \item Bei TrueColor: \texttt{return (red << 16) + (green << 8) + blue;}
410         \item Alles andere: Round-Trip zum X-Server:
411 \end{list}
412 \begin{code}
413         #define RGB_8_TO_16(i) (65535 * ((i) & 0xFF) / 255)
414         xcb_alloc_color_reply_t *reply;
415         reply = xcb_alloc_color_reply(conn, xcb_alloc_color(conn,
416                 root_screen->default_colormap, RGB_8_TO_16(red),
417                 RGB_8_TO_16(green), RGB_8_TO_16(blue)), NULL);
418         if (!reply)
419                 die("Could not allocate color\n");
420         return reply->pixel;
421 \end{code}
422 \isrc{i3/src/xcb.c:76ff}
423 \end{slide}
424
425 \begin{slide}[method=direct]{Hints}
426 \begin{list}{$\bullet$}{\itemsep=.5em}
427         \item NetWM
428         \begin{description}
429                 \item[NET\_WM\_WINDOW\_TYPE] dock, dialog, utility, toolbar, splashscreen
430                 \item[NET\_WM\_NAME] Fenstertitel (UTF-8), auch auf dem root-Fenster
431                 \item[NET\_WM\_STRUT\_PARTIAL] Reservierter Bereich am Bildschirmrand (Docks), z.B. für dzen2
432         \end{description}
433         \item ICCCM
434         \begin{description}
435                 \item[WM\_NAME] Fenstertitel (Compound Text)
436                 \item[WM\_TRANSIENT\_FOR] Zugehöriges, "`temporäres"' Fenster für Anwendung X ($\Rightarrow$ floating)
437                 \item[WM\_CLASS] Fensterklasse (z.B. "`urxvt"'), praktisch zum identifizieren
438                 \item[WM\_NORMAL\_HINTS] (Size hints), beinhaltet Aspect Ratio (mplayer!), minimale und maximale Größe
439         \end{description}
440 \end{list}
441 \end{slide}
442
443 \begin{slide}[method=direct]{Hints (2)}
444 \begin{code}
445 int handle_transient_for(void *data, xcb_connection_t *conn,
446            uint8_t state, xcb_window_t window,
447            xcb_atom_t name, xcb_get_property_reply_t *reply)
448 {
449   xcb_window_t transient_for;
450   if (reply != NULL) {
451     if (!xcb_get_wm_transient_for_from_reply(&transient_for, reply)) {
452       LOG("Not transient for any window\n");
453       return 1;
454     }
455   } else {
456     if (!xcb_get_wm_transient_for_reply(conn,
457         xcb_get_wm_transient_for_unchecked(conn, window),
458         &transient_for, NULL)) {
459       LOG("Not transient for any window\n");
460       return 1;
461     }
462   }
463   if (client->floating == FLOATING_AUTO_OFF)
464     toggle_floating_mode(conn, client, true);
465   return 1;
466 }
467 \end{code}
468 \end{slide}
469
470 \begin{slide}[method=direct]{Gotchas}
471 \begin{list}{$\bullet$}{\itemsep=.5em}
472         \item Flushing (\texttt{xcb\_flush(connection);})
473         \item \texttt{WM\_STATE} != \texttt{WM\_STATE\_NORMAL}
474         \item Eventloops / Caching von xcb (GIMP splash screen)
475 \end{list}
476 \end{slide}
477
478
479 \begin{slide}{Zusammenfassung}
480 \begin{list}{$\bullet$}{\itemsep=.5em}
481         \item Bindings aufsetzen, Eventmask konfigurieren
482         \item Events/Hints abarbeiten
483         \item Decorations zeichnen
484 \end{list}
485 \end{slide}
486
487 \begin{slide}{Lust bekommen?}
488 \begin{list}{$\bullet$}{\itemsep=1em}
489         \item git clone \url{git://code.stapelberg.de/i3}
490         \item development branch: \texttt{git checkout --track -b next origin/next}
491         \item Debian: \texttt{apt-get install i3-wm/unstable}
492         \item non-Debian: \texttt{cd i3; cat DEPENDS; make \&\& sudo make install}
493         \item in \~{}/.xsession: \texttt{exec /usr/bin/i3}
494         \item Siehe manpage \texttt{i3(1)}, user’s guide, how to hack
495 \end{list}
496 \end{slide}
497
498 \begin{slide}{exit(0);}
499 \begin{list}{$\bullet$}{\itemsep=1em}
500         \item git-webinterface: \url{http://code.stapelberg.de/git/i3}
501         \item Website: \url{http://i3wm.org}
502         \item IRC: \#i3 auf irc.twice-irc.de
503         \item xcb: \url{http://xcb.freedesktop.org/}
504         \item 50-Zeilen-WM: \url{http://incise.org/tinywm.html}
505         \item „Why X is not our ideal window system”: \url{http://www.std.org/~msm/common/WhyX.pdf}
506         \item …noch Fragen?
507 \end{list}
508 \end{slide}
509
510 \end{document}