Project 5: X11 Comparison Project - Bare-Metal X11 Client

Project 5: X11 Comparison Project - Bare-Metal X11 Client

Project Overview

Attribute Value
Difficulty Intermediate (Level 2)
Time Estimate 1 week
Programming Language C (also: C++, Rust, Zig)
Knowledge Area Graphics, Windowing Systems
Main Book Xlib Programming Manual (Oโ€™Reilly)
Coolness Level Level 3: Genuinely Clever
Business Potential Resume Gold

What youโ€™ll build: The exact same colored window as Project 1, but using raw Xlib to viscerally feel the differences between X11 and Wayland.

Why it teaches X11 vs Wayland: Nothing teaches the difference better than implementing the same thing in both. Youโ€™ll feel X11โ€™s complexity (window properties, atoms, event masks) vs Waylandโ€™s simplicity (but also Waylandโ€™s โ€œyou do the workโ€ philosophy).


Learning Objectives

By completing this project, you will be able to:

  1. Contrast X11 and Wayland architectures - Explain the fundamental differences in design philosophy
  2. Implement X11 client basics - Connect to display, create window, handle events
  3. Understand X11 atoms and properties - Use the property system for window metadata
  4. Handle Expose events - Implement the X11 redraw model
  5. Work with ICCCM/EWMH - Use window manager hints correctly
  6. Appreciate Waylandโ€™s design choices - Understand why Wayland was created

The Core Question Youโ€™re Answering

โ€œWhat was so broken about X11 that the entire Linux graphics stack was redesigned from scratch, and what does the difference feel like in code?โ€

By implementing the same window in both X11 and Wayland, youโ€™ll experience:

  • X11โ€™s server-side rendering model: You tell the server โ€œdraw a rectangle hereโ€
  • Waylandโ€™s client-side rendering: You render pixels yourself and share the buffer
  • X11โ€™s complexity: Atoms, properties, event masks, window hints
  • Waylandโ€™s minimalism: Just surfaces, buffers, and protocols
  • X11โ€™s security model: Any client can read any window
  • Waylandโ€™s isolation: Clients canโ€™t see each other

The side-by-side comparison will cement your understanding of both systems and explain why Wayland exists.


Deep Theoretical Foundation

1. X11 vs Wayland Architecture Comparison

X11 ARCHITECTURE
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

         Client A          Client B          Client C
             โ”‚                 โ”‚                 โ”‚
             โ”‚    X11 Protocol โ”‚                 โ”‚
             โ”‚    (Network)    โ”‚                 โ”‚
             โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                               โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚      X Server       โ”‚
                    โ”‚                     โ”‚
                    โ”‚  โ€ข Resource mgmt    โ”‚
                    โ”‚  โ€ข Draw execution   โ”‚
                    โ”‚  โ€ข Event routing    โ”‚
                    โ”‚  โ€ข Window storage   โ”‚  โ† Server stores window contents!
                    โ”‚  โ€ข Input handling   โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                               โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚     Compositor      โ”‚  โ† Separate process
                    โ”‚  (Compiz, Picom)    โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                               โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚        GPU          โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜


WAYLAND ARCHITECTURE
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

         Client A          Client B          Client C
             โ”‚                 โ”‚                 โ”‚
             โ”‚  Each client    โ”‚                 โ”‚
             โ”‚  renders own    โ”‚                 โ”‚
             โ”‚  content!       โ”‚                 โ”‚
             โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                               โ”‚  Buffer FDs
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚  Wayland Compositor โ”‚
                    โ”‚  (Display Server +  โ”‚
                    โ”‚   Window Manager +  โ”‚
                    โ”‚   Compositor)       โ”‚  โ† All-in-one!
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                               โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚        GPU          โ”‚
                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜


KEY DIFFERENCE: Who renders?
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

X11:
  Client: "X server, please draw a red rectangle at (10, 10) size (100, 50)"
  Server: *draws rectangle* *stores in window pixmap*
  Result: Server has all the window contents in memory

Wayland:
  Client: *creates buffer* *paints red rectangle* "Compositor, here's my buffer FD"
  Compositor: *composites buffer to screen* *doesn't copy or store*
  Result: Client owns its pixels, compositor just displays them

X11 vs Wayland Architecture Comparison

2. X11 Concepts You Must Understand

X11 CORE CONCEPTS
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

DISPLAY
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  - Connection to X server (local Unix socket or network)
  - Format: hostname:display.screen (e.g., ":0.0")
  - Multiple screens possible (rare today)
  - $DISPLAY environment variable

WINDOW
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  - Rectangular area on screen
  - Has parent (tree structure, root at top)
  - Has attributes (size, position, border, background)
  - Can be InputOutput (visible) or InputOnly (invisible grab)

PIXMAP
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  - Off-screen drawable
  - Same format as window
  - Used for double buffering, icons

GRAPHICS CONTEXT (GC)
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  - Holds drawing parameters (color, font, line style)
  - Reused across draw operations for efficiency
  - Created per-connection

ATOMS
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  - Interned strings โ†’ integer IDs
  - Used for property names, selections, etc.
  - Standard atoms: WM_NAME, WM_CLASS, _NET_WM_STATE
  - Created with XInternAtom()

PROPERTIES
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  - Key-value storage on windows
  - Key = atom, Value = typed data
  - Window manager reads properties to know what to do
  - XChangeProperty(), XGetWindowProperty()

EVENT MASKS
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  - Each window can receive different events
  - Set with XSelectInput()
  - ExposureMask, KeyPressMask, ButtonPressMask, StructureNotifyMask

3. The Expose Event Model

X11 uses an โ€œExposeโ€ event model for repainting:

X11 EXPOSE EVENT FLOW
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Initial window creation:
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  1. Client creates window: XCreateWindow()
  2. Client maps window: XMapWindow()
  3. Window appears, possibly obscured
  4. X server notices window is now visible
  5. X server sends Expose event to client
  6. Client receives Expose, draws content

Window uncovered:
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  1. User moves overlapping window away
  2. X server notices previously obscured region now visible
  3. X server sends Expose event(s) for damaged regions
  4. Client must redraw exposed regions

  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚                                         โ”‚
  โ”‚     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                    โ”‚
  โ”‚     โ”‚   Window B   โ”‚                    โ”‚
  โ”‚     โ”‚  (on top)    โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
  โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜               โ”‚    โ”‚
  โ”‚           โ”‚                        โ”‚    โ”‚
  โ”‚     User moves B โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
  โ”‚           โ”‚                             โ”‚
  โ”‚           โ–ผ                             โ”‚
  โ”‚     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                    โ”‚
  โ”‚     โ”‚   Window A   โ”‚ โ† Expose event!   โ”‚
  โ”‚     โ”‚  (needs      โ”‚   Regions that    โ”‚
  โ”‚     โ”‚   redraw)    โ”‚   were covered    โ”‚
  โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                    โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Contrast with Wayland:
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  - Wayland: Client always owns its buffer, no Expose events
  - Wayland: Compositor has the pixels, just re-composites
  - Wayland: Client only redraws when it wants to change content

X11 Expose Event Flow

4. Window Manager Hints (ICCCM/EWMH)

X11 uses properties for window manager communication:

WINDOW MANAGER COMMUNICATION
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

ICCCM (Inter-Client Communication Conventions Manual):
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  WM_NAME         - Window title (Latin-1 encoded)
  WM_CLASS        - Instance and class names (for window matching)
  WM_NORMAL_HINTS - Size hints (min, max, aspect ratio)
  WM_HINTS        - Urgency, icon, input focus model
  WM_PROTOCOLS    - Supported protocols (WM_DELETE_WINDOW)
  WM_STATE        - Window state (Normal, Iconic, Withdrawn)

EWMH (Extended Window Manager Hints):
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  _NET_WM_NAME          - UTF-8 window title
  _NET_WM_STATE         - Fullscreen, maximized, sticky, etc.
  _NET_WM_WINDOW_TYPE   - Normal, dialog, dock, desktop, etc.
  _NET_WM_PID           - Process ID of client
  _NET_WM_DESKTOP       - Which desktop/workspace
  _NET_ACTIVE_WINDOW    - The currently focused window

Setting properties in X11:
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  Atom wm_name = XInternAtom(display, "WM_NAME", False);
  XChangeProperty(display, window, wm_name, XA_STRING, 8,
                  PropModeReplace, (unsigned char*)"My Window", 9);

  // For UTF-8 title (EWMH):
  Atom net_wm_name = XInternAtom(display, "_NET_WM_NAME", False);
  Atom utf8 = XInternAtom(display, "UTF8_STRING", False);
  XChangeProperty(display, window, net_wm_name, utf8, 8,
                  PropModeReplace, (unsigned char*)"My Window", 9);

Contrast with Wayland:
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  xdg_toplevel_set_title(toplevel, "My Window");
  // One function call, UTF-8 by default, no atoms

Complete Project Specification

Functional Requirements

  1. Display Connection: Connect to $DISPLAY (or default)
  2. Window Creation: Create a top-level window
  3. Event Handling: Handle Expose, ConfigureNotify, ClientMessage
  4. Rendering: Fill window with solid color
  5. WM Integration: Set WM_NAME, handle WM_DELETE_WINDOW
  6. Resize Handling: Respond to window resize

Expected Behavior

Same as Project 1:

  • Window appears with solid color
  • Can be resized, moved, minimized
  • Closes properly when user closes window
  • Handles resize by repainting

Solution Architecture

Minimal X11 Client Structure

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct x11_state {
    Display *display;
    int screen;
    Window root;
    Window window;
    GC gc;
    Atom wm_delete_window;
    int width, height;
    unsigned long color;
    int running;
};

Event Loop Pattern

void run_event_loop(struct x11_state *state) {
    XEvent event;

    while (state->running) {
        XNextEvent(state->display, &event);

        switch (event.type) {
        case Expose:
            if (event.xexpose.count == 0) {
                // Last expose in series, redraw
                redraw(state);
            }
            break;

        case ConfigureNotify:
            if (event.xconfigure.width != state->width ||
                event.xconfigure.height != state->height) {
                state->width = event.xconfigure.width;
                state->height = event.xconfigure.height;
                // Resize triggers Expose, so redraw happens
            }
            break;

        case ClientMessage:
            if ((Atom)event.xclient.data.l[0] == state->wm_delete_window) {
                state->running = 0;
            }
            break;
        }
    }
}

Phased Implementation Guide

Phase 1: Connect and Create Window (Day 1-2)

Goal: Open display, create window, see it appear

Steps:

  1. XOpenDisplay(NULL)
  2. Get screen and root window
  3. XCreateSimpleWindow() with background color
  4. XSelectInput() for events
  5. XMapWindow()
  6. XFlush() and verify window appears

Code:

Display *display = XOpenDisplay(NULL);
if (!display) {
    fprintf(stderr, "Cannot open display\n");
    exit(1);
}

int screen = DefaultScreen(display);
Window root = RootWindow(display, screen);

Window window = XCreateSimpleWindow(
    display, root,
    100, 100,    // x, y
    800, 600,    // width, height
    1,           // border width
    BlackPixel(display, screen),  // border
    0xFF0000     // background (red)
);

XSelectInput(display, window,
    ExposureMask | StructureNotifyMask | KeyPressMask);

XMapWindow(display, window);
XFlush(display);

Phase 2: Window Manager Integration (Day 2-3)

Goal: Set window title, handle close button

Steps:

  1. Set WM_NAME property
  2. Set _NET_WM_NAME for UTF-8
  3. Register for WM_DELETE_WINDOW protocol
  4. Handle ClientMessage event

Code:

// Set title
XStoreName(display, window, "My X11 Client");

// Handle close button
Atom wm_protocols = XInternAtom(display, "WM_PROTOCOLS", False);
Atom wm_delete = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, &wm_delete, 1);

// In event loop:
if (event.xclient.message_type == wm_protocols &&
    (Atom)event.xclient.data.l[0] == wm_delete) {
    running = 0;
}

Phase 3: Drawing with GC (Day 3-4)

Goal: Draw solid color in Expose handler

Steps:

  1. Create Graphics Context
  2. Set foreground color
  3. Handle Expose event
  4. XFillRectangle() to draw

Code:

GC gc = XCreateGC(display, window, 0, NULL);
XSetForeground(display, gc, 0xFF0000);  // Red

// In Expose handler:
XFillRectangle(display, window, gc, 0, 0, width, height);

Phase 4: Resize Handling (Day 4-5)

Goal: Redraw on resize

Steps:

  1. Handle ConfigureNotify event
  2. Store new dimensions
  3. Redraw (Expose will follow)

Phase 5: Comparison Analysis (Day 5-7)

Goal: Document differences from Wayland

Exercise: Create a comparison table of:

  • Lines of code
  • Concepts required
  • Error handling
  • Security implications
  • Debugging difficulty

Side-by-Side Code Comparison

Window Creation

X11                                    Wayland
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Display *d = XOpenDisplay(NULL);       struct wl_display *d =
                                           wl_display_connect(NULL);

int s = DefaultScreen(d);              struct wl_registry *r =
Window root = RootWindow(d, s);            wl_display_get_registry(d);
                                       // Bind to wl_compositor

Window w = XCreateSimpleWindow(        struct wl_surface *s =
    d, root, 0, 0, 800, 600,               wl_compositor_create_surface(c);
    0, 0, 0xFF0000);                   struct xdg_surface *xs =
                                           xdg_wm_base_get_xdg_surface(b, s);
                                       struct xdg_toplevel *t =
                                           xdg_surface_get_toplevel(xs);

XMapWindow(d, w);                      wl_surface_commit(s);
                                       // Wait for configure, create buffer,
                                       // paint pixels, attach, commit

Setting Window Title

X11                                    Wayland
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

// ICCCM (Latin-1)                     xdg_toplevel_set_title(t, "Title");
XStoreName(d, w, "Title");             // That's it!

// EWMH (UTF-8)
Atom a = XInternAtom(d, "_NET_WM_NAME", False);
Atom utf8 = XInternAtom(d, "UTF8_STRING", False);
XChangeProperty(d, w, a, utf8, 8,
    PropModeReplace, "Title", 5);

Drawing

X11                                    Wayland
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

// Server-side drawing                 // Client-side rendering
GC gc = XCreateGC(d, w, 0, NULL);     int fd = memfd_create("buf", 0);
XSetForeground(d, gc, 0xFF0000);       ftruncate(fd, size);
XFillRectangle(d, w, gc, 0, 0,        void *data = mmap(...);
    width, height);
                                       // Paint pixels yourself
                                       uint32_t *px = data;
                                       for (int i = 0; i < w*h; i++)
                                           px[i] = 0xFFFF0000;

                                       // Create buffer, attach, commit
                                       wl_surface_attach(s, buf, 0, 0);
                                       wl_surface_commit(s);

Handling Resize

X11                                    Wayland
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

// Receive ConfigureNotify event      // Receive xdg_toplevel.configure
case ConfigureNotify:                  static void handle_configure(
    width = e.xconfigure.width;            void *data, ..., int w, int h) {
    height = e.xconfigure.height;          state->width = w;
    // Expose will follow,                 state->height = h;
    // redraw there                        // Create new buffer at new size
    break;                                 // Paint, attach, ack, commit
                                       }
case Expose:
    if (e.xexpose.count == 0)
        XFillRectangle(...);

Key Differences Summary

ARCHITECTURAL DIFFERENCES
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Aspect              | X11                    | Wayland
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Rendering           | Server-side            | Client-side
Buffer ownership    | Server stores          | Client stores
Redraw model        | Expose events          | Client-initiated
Text encoding       | Latin-1 + atoms        | UTF-8 everywhere
WM communication    | Properties + atoms     | Protocol requests
Security            | Any client can snoop   | Isolated by design
Complexity          | Many concepts          | Fewer, cleaner
Network support     | Built-in               | Not designed for it
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

CODE COMPARISON
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Metric              | X11                    | Wayland
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Lines of code       | ~100                   | ~300
Concepts needed     | 8-10                   | 5-6
Headers included    | 3                      | 2 + generated
Library calls       | ~15                    | ~20
Error points        | Many                   | Clearer
Debugging           | xev, xprop, xwininfo   | WAYLAND_DEBUG=1
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

Testing Strategy

Functional Testing

Test X11 Wayland
Window appears Yes Yes
Correct color Yes Yes
Resize works Yes Yes
Title shows Yes Yes
Close button works Yes Yes

Environment Testing

# X11
$ ./x11_client
# Should work on X11 session

# Test XWayland
$ DISPLAY=:0 ./x11_client
# Should work on Wayland with XWayland

# Wayland
$ ./wayland_client
# Should work on Wayland session only

Common Pitfalls and Debugging

X11-Specific Issues

Problem: Window appears then disappears

Cause: Program exits before event loop Fix: Add event loop with XNextEvent()

Problem: No title showing

Cause: Not setting WM_NAME or window manager not reading it Fix: Also set _NET_WM_NAME (EWMH)

Problem: Close button doesnโ€™t work

Cause: Not handling WM_DELETE_WINDOW Fix: XSetWMProtocols() and check ClientMessage

Debugging Tools

# Show window properties
$ xprop -id <window_id>

# Watch events
$ xev

# Show window tree
$ xwininfo -root -tree

# Protocol trace
$ xtrace ./x11_client

Interview Questions Comparison

After completing both P01 and P05, you should be able to answer:

  1. โ€œWhy was Wayland created? What problems does it solve?โ€
    • X11โ€™s server-side rendering is inefficient for modern GPUs
    • X11โ€™s security model allows any client to snoop
    • X11 is designed for network, wasteful for local
    • Wayland is simpler, more secure, better performance
  2. โ€œWhatโ€™s the fundamental difference in rendering?โ€
    • X11: Client sends draw commands, server executes and stores
    • Wayland: Client renders to buffer, compositor displays
  3. โ€œHow does resize handling differ?โ€
    • X11: ConfigureNotify โ†’ Expose โ†’ Client redraws to server
    • Wayland: xdg_toplevel.configure โ†’ Client creates new buffer โ†’ attach โ†’ commit
  4. โ€œWhy is Wayland more secure?โ€
    • X11: Any client can XGetImage any window
    • Wayland: Clients isolated, canโ€™t see each otherโ€™s buffers
  5. โ€œWhatโ€™s the trade-off?โ€
    • X11: Less client work, network transparent, mature
    • Wayland: More client work, better performance/security, modern

Resources

X11 Documentation

Resource Purpose
Xlib Programming Manual Complete reference
ICCCM specification Window manager hints
EWMH specification Modern WM hints
Xlib Reference Manual Function reference

Online Resources

  • https://tronche.com/gui/x/xlib/ - Xlib reference
  • freedesktop.org ICCCM/EWMH specs
  • X.org documentation

Self-Assessment Checklist

Before considering this project complete, verify you can:

  • Explain why X11 uses Expose events but Wayland doesnโ€™t
  • Describe what an X11 atom is and why they exist
  • Write an X11 client from scratch
  • Handle WM_DELETE_WINDOW correctly
  • Compare lines of code and complexity with Wayland version
  • Debug X11 issues using xprop, xev, xwininfo
  • Explain the security difference between X11 and Wayland
  • Describe the rendering model difference
  • Run your X11 client under XWayland
  • Articulate why Wayland was created

Completing this project alongside P01 gives you deep understanding of both display server architectures. You can now speak authoritatively about X11 vs Wayland, understanding the trade-offs and reasons behind Waylandโ€™s design. This comparative knowledge is valuable in any Linux desktop or graphics role.