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"]].bytes, 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.fromBytes!(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 }