1 /**
2  * X11 demo.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 module ae.demo.x11.demo;
15 
16 import std.algorithm.iteration;
17 import std.array;
18 
19 import ae.net.asockets;
20 import ae.net.x11;
21 import ae.utils.array;
22 import ae.utils.promise;
23 
24 // Pro tip: Build with -debug=PRINTDATA to see the raw bytes sent and
25 // received from the X server!
26 
27 void main()
28 {
29 	auto x11 = new X11Client();
30 
31 	// Maintain a dictionary of known interred atoms.
32 	Atom[string] atoms;
33 	Promise!Atom getAtom(string name)
34 	{
35 		auto p = new Promise!Atom;
36 		if (auto patom = name in atoms)
37 			p.fulfill(*patom);
38 		else
39 			x11.sendInternAtom(false, name)
40 				.dmd21804workaround
41  				.then((result) {
42 					atoms[name] = result.atom;
43 					p.fulfill(result.atom);
44 				});
45 		return p;
46 	}
47 
48 	// Our window, and the context we use to draw on it.
49 	Window wid;
50 	GContext gc;
51 
52 	// All operations can only be done once the handshake completes.
53 	x11.handleConnect = {
54 		wid = x11.newRID(); // ID generation happens completely locally.
55 
56 		// Create our window.
57 		WindowAttributes windowAttributes;
58 		windowAttributes.eventMask = ExposureMask;
59 		x11.sendCreateWindow(
60 			0,
61 			wid,
62 			x11.roots[0].root.windowId,
63 			0, 0,
64 			256, 256,
65 			10,
66 			InputOutput,
67 			x11.roots[0].root.rootVisualID,
68 			windowAttributes,
69 		);
70 		x11.sendMapWindow(wid);
71 
72 		// Create a graphics context from the window.
73 		gc = x11.newRID();
74 		GCAttributes gcAttributes;
75 		gcAttributes.foreground = x11.roots[0].root.blackPixel;
76 		gcAttributes.background = x11.roots[0].root.whitePixel;
77 		x11.sendCreateGC(
78 			gc, wid,
79 			gcAttributes,
80 		);
81 
82 		// Announce our support of the WM_DELETE_WINDOW window manager
83 		// protocol.  To do that, we need to intern some atoms first.
84 		["WM_PROTOCOLS", "WM_DELETE_WINDOW", "ATOM"]
85 			.map!getAtom
86 			.array
87 			.all
88 			.then((result) {
89 				x11.sendChangeProperty(
90 					PropModeReplace,
91 					wid,
92 					atoms["WM_PROTOCOLS"], atoms["ATOM"],
93 					32,
94 					[atoms["WM_DELETE_WINDOW"]].asBytes,
95 				);
96 			});
97 	};
98 
99 	// The expose event informs us when it's time to repaint our window.
100 	// Register a handler here.
101 	x11.handleExpose = (event) {
102 		if (event.window == wid)
103 		{
104 			x11.sendPolyFillRectangle(wid, gc, [xRectangle(0, 0, ushort.max, ushort.max)]);
105 			// Query the current window geometry, so that we can draw the text in the center.
106 			x11.sendGetGeometry(wid)
107 				.dmd21804workaround
108 				.then((result) {
109 					x11.sendImageText8(wid, gc, result.width / 2, result.height / 2, "Hello X11!");
110 				});
111 		}
112 	};
113 
114 	// Register a handler for the client message event, so that we can
115 	// be notified of when the window manager is asking our window to
116 	// please go away.
117 	x11.handleClientMessage = (event) {
118 		if (event.type == atoms["WM_PROTOCOLS"])
119 		{
120 			auto messageAtoms = event.bytes.as!(Atom[]);
121 			if (messageAtoms[0] == atoms["WM_DELETE_WINDOW"])
122 			{
123 				// As the X11 connection is the only object in the
124 				// event loop, disconnecting from the X server will
125 				// gracefully stop our application.
126 				x11.conn.disconnect();
127 			}
128 		}
129 	};
130 	x11.handleDisconnect = (string error, DisconnectType type) {
131 		import std.stdio : writefln;
132 		writefln("Disconnected (%s): %s", type, error);
133 	};
134 
135 	// Run the event loop.
136 	socketManager.loop();
137 }