1 // Example create-window shows how to create a window, map it, resize it, 2 // and listen to structure and key events (i.e., when the window is resized 3 // by the window manager, or when key presses/releases are made when the 4 // window has focus). The events are printed to stdout. 5 package main 6 7 import ( 8 "fmt" 9 10 "github.com/jezek/xgb" 11 "github.com/jezek/xgb/xproto" 12 ) 13 14 func main() { 15 X, err := xgb.NewConn() 16 if err != nil { 17 fmt.Println(err) 18 return 19 } 20 defer X.Close() 21 22 // xproto.Setup retrieves the Setup information from the setup bytes 23 // gathered during connection. 24 setup := xproto.Setup(X) 25 26 // This is the default screen with all its associated info. 27 screen := setup.DefaultScreen(X) 28 29 // Any time a new resource (i.e., a window, pixmap, graphics context, etc.) 30 // is created, we need to generate a resource identifier. 31 // If the resource is a window, then use xproto.NewWindowId. If it's for 32 // a pixmap, then use xproto.NewPixmapId. And so on... 33 wid, _ := xproto.NewWindowId(X) 34 35 // CreateWindow takes a boatload of parameters. 36 xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root, 37 0, 0, 500, 500, 0, 38 xproto.WindowClassInputOutput, screen.RootVisual, 0, []uint32{}) 39 40 // This call to ChangeWindowAttributes could be factored out and 41 // included with the above CreateWindow call, but it is left here for 42 // instructive purposes. It tells X to send us events when the 'structure' 43 // of the window is changed (i.e., when it is resized, mapped, unmapped, 44 // etc.) and when a key press or a key release has been made when the 45 // window has focus. 46 // We also set the 'BackPixel' to white so that the window isn't butt ugly. 47 xproto.ChangeWindowAttributes(X, wid, 48 xproto.CwBackPixel|xproto.CwEventMask, 49 []uint32{ // values must be in the order defined by the protocol 50 0xffffffff, 51 xproto.EventMaskStructureNotify | 52 xproto.EventMaskKeyPress | 53 xproto.EventMaskKeyRelease, 54 }) 55 56 // MapWindow makes the window we've created appear on the screen. 57 // We demonstrated the use of a 'checked' request here. 58 // A checked request is a fancy way of saying, "do error handling 59 // synchronously." Namely, if there is a problem with the MapWindow request, 60 // we'll get the error *here*. If we were to do a normal unchecked 61 // request (like the above CreateWindow and ChangeWindowAttributes 62 // requests), then we would only see the error arrive in the main event 63 // loop. 64 // 65 // Typically, checked requests are useful when you need to make sure they 66 // succeed. Since they are synchronous, they incur a round trip cost before 67 // the program can continue, but this is only going to be noticeable if 68 // you're issuing tons of requests in succession. 69 // 70 // Note that requests without replies are by default unchecked while 71 // requests *with* replies are checked by default. 72 err = xproto.MapWindowChecked(X, wid).Check() 73 if err != nil { 74 fmt.Printf("Checked Error for mapping window %d: %s\n", wid, err) 75 } else { 76 fmt.Printf("Map window %d successful!\n", wid) 77 } 78 79 // This is an example of an invalid MapWindow request and what an error 80 // looks like. 81 err = xproto.MapWindowChecked(X, 0).Check() 82 if err != nil { 83 fmt.Printf("Checked Error for mapping window 0x1: %s\n", err) 84 } else { // neva 85 fmt.Printf("Map window 0x1 successful!\n") 86 } 87 88 // Start the main event loop. 89 for { 90 // WaitForEvent either returns an event or an error and never both. 91 // If both are nil, then something went wrong and the loop should be 92 // halted. 93 // 94 // An error can only be seen here as a response to an unchecked 95 // request. 96 ev, xerr := X.WaitForEvent() 97 if ev == nil && xerr == nil { 98 fmt.Println("Both event and error are nil. Exiting...") 99 return 100 } 101 102 if ev != nil { 103 fmt.Printf("Event: %s\n", ev) 104 } 105 if xerr != nil { 106 fmt.Printf("Error: %s\n", xerr) 107 } 108 109 // This is how accepting events work: 110 // The application checks what event we got and reacts to it 111 // accordingly. All events are defined in the xproto subpackage. 112 // To receive events, we have to first register it using 113 // either xproto.CreateWindow or xproto.ChangeWindowAttributes. 114 switch ev.(type) { 115 case xproto.KeyPressEvent: 116 // See https://pkg.go.dev/github.com/jezek/xgb/xproto#KeyPressEvent 117 // for documentation about a key press event. 118 kpe := ev.(xproto.KeyPressEvent) 119 fmt.Printf("Key pressed: %d\n", kpe.Detail) 120 // The Detail value depends on the keyboard layout, 121 // for QWERTY, q is #24. 122 if kpe.Detail == 24 { 123 return // exit on q 124 } 125 case xproto.DestroyNotifyEvent: 126 // Depending on the user's desktop environment (especially 127 // window manager), killing a window might close the 128 // client's X connection (e. g. the default Ubuntu 129 // desktop environment). 130 // 131 // If that's the case for your environment, closing this example's window 132 // will also close the underlying Go program (because closing the X 133 // connection gives a nil event and EOF error). 134 // 135 // Consider how a single application might have multiple windows 136 // (e.g. an open popup or dialog, ...) 137 // 138 // With other DEs, the X connection will still stay open even after the 139 // X window is closed. For these DEs (e.g. i3) we have to check whether 140 // the WM sent us a DestroyNotifyEvent and close our program. 141 // 142 // For more information about closing windows while maintaining 143 // the X connection see 144 // https://github.com/jezek/xgbutil/blob/master/_examples/graceful-window-close/main.go 145 return 146 } 147 } 148 } 149