From 2a6cba9c3c2e3d2ea720099b61b39669ff6b7474 Mon Sep 17 00:00:00 2001 From: scrouthtv Date: Mon, 1 Mar 2021 18:28:23 +0100 Subject: Update main.go added example of reading events --- examples/create-window/main.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/create-window/main.go b/examples/create-window/main.go index 50a9bff..284daef 100644 --- a/examples/create-window/main.go +++ b/examples/create-window/main.go @@ -96,12 +96,22 @@ func main() { fmt.Println("Both event and error are nil. Exiting...") return } - + if ev != nil { fmt.Printf("Event: %s\n", ev) } if xerr != nil { fmt.Printf("Error: %s\n", xerr) } + + // This is how accepting events work: + // The application checks what event we got + // (the event must be registered using either xproto.CreateWindow (see l.35) or + // xproto.ChangeWindowAttributes (see l.50)). + // and reacts to it accordingly. All events are defined in the xproto subpackage. + switch ev.(type) { + case xproto.DestroyNotifyEvent: + return // Exit if we get a DestroyNotifyEvent. + } } } -- cgit v1.2.3 From 4c1bf8cd8d4f0f96428a90f3117896c7f1564cdf Mon Sep 17 00:00:00 2001 From: Lenni Date: Wed, 3 Mar 2021 19:04:54 +0100 Subject: added listening to key presses --- examples/create-window/main.go | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'examples') diff --git a/examples/create-window/main.go b/examples/create-window/main.go index 284daef..76df734 100644 --- a/examples/create-window/main.go +++ b/examples/create-window/main.go @@ -110,6 +110,16 @@ func main() { // xproto.ChangeWindowAttributes (see l.50)). // and reacts to it accordingly. All events are defined in the xproto subpackage. switch ev.(type) { + case xproto.KeyPressEvent: + // See https://pkg.go.dev/github.com/jezek/xgb@v0.0.0-20210121230032-cec22bda1ce1/xproto#KeyPressEvent + // for documentation about a key press event. + kpe := ev.(xproto.KeyPressEvent) + fmt.Printf("Key pressed: %d", kpe.Detail) + // The Detail value depends on the keyboard layout, + // for QWERTY, q is #24. + if kpe.Detail == 24 { + return // exit on q + } case xproto.DestroyNotifyEvent: return // Exit if we get a DestroyNotifyEvent. } -- cgit v1.2.3 From 2825966d27b67b6b0227bcbfcd81f3c995c0f472 Mon Sep 17 00:00:00 2001 From: Lenni Date: Wed, 3 Mar 2021 19:05:58 +0100 Subject: formatting --- examples/create-window/main.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/create-window/main.go b/examples/create-window/main.go index 76df734..d4f6d96 100644 --- a/examples/create-window/main.go +++ b/examples/create-window/main.go @@ -49,7 +49,8 @@ func main() { 0xffffffff, xproto.EventMaskStructureNotify | xproto.EventMaskKeyPress | - xproto.EventMaskKeyRelease}) + xproto.EventMaskKeyRelease, + }) // MapWindow makes the window we've created appear on the screen. // We demonstrated the use of a 'checked' request here. @@ -96,14 +97,14 @@ func main() { fmt.Println("Both event and error are nil. Exiting...") return } - + if ev != nil { fmt.Printf("Event: %s\n", ev) } if xerr != nil { fmt.Printf("Error: %s\n", xerr) } - + // This is how accepting events work: // The application checks what event we got // (the event must be registered using either xproto.CreateWindow (see l.35) or @@ -115,8 +116,8 @@ func main() { // for documentation about a key press event. kpe := ev.(xproto.KeyPressEvent) fmt.Printf("Key pressed: %d", kpe.Detail) - // The Detail value depends on the keyboard layout, - // for QWERTY, q is #24. + // The Detail value depends on the keyboard layout, + // for QWERTY, q is #24. if kpe.Detail == 24 { return // exit on q } -- cgit v1.2.3 From 89052459e19439240a5f3a1d3e267650f0d0c554 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 17:46:57 +0100 Subject: Added explanation to the whole DestroyNotifyEvent thing. See https://github.com/jezek/xgb/pull/1 --- examples/create-window/main.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/create-window/main.go b/examples/create-window/main.go index d4f6d96..9510f9c 100644 --- a/examples/create-window/main.go +++ b/examples/create-window/main.go @@ -17,6 +17,7 @@ func main() { fmt.Println(err) return } + defer X.Close() // xproto.Setup retrieves the Setup information from the setup bytes // gathered during connection. @@ -122,7 +123,22 @@ func main() { return // exit on q } case xproto.DestroyNotifyEvent: - return // Exit if we get a DestroyNotifyEvent. + // Depending on the user's desktop environment (especially + // window manager), killing a window might close the + // client's X connection (e. g. the default Ubuntu + // desktop environment). + // + // If that's the case for your environment, closing this example's window + // will also call the underlying Go program (because closing the X + // connection gives a nil event and EOF error). + // + // Consider how a single application might have multiple windows + // (e.g. an open popup or dialog, ...) + // + // With other DEs, the X connection will still stay open even after the + // X window is closed. For these DEs (e.g. i3) we have to check whether + // the WM sent us a DestroyNotifyEvent and close our program: + return } } } -- cgit v1.2.3 From 35e3533c4edb11d08cc36fa9a524df38d7ab5f57 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 17:47:38 +0100 Subject: formatting --- examples/create-window/main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/create-window/main.go b/examples/create-window/main.go index 9510f9c..ef7d98a 100644 --- a/examples/create-window/main.go +++ b/examples/create-window/main.go @@ -125,16 +125,16 @@ func main() { case xproto.DestroyNotifyEvent: // Depending on the user's desktop environment (especially // window manager), killing a window might close the - // client's X connection (e. g. the default Ubuntu + // client's X connection (e. g. the default Ubuntu // desktop environment). - // + // // If that's the case for your environment, closing this example's window // will also call the underlying Go program (because closing the X // connection gives a nil event and EOF error). - // + // // Consider how a single application might have multiple windows // (e.g. an open popup or dialog, ...) - // + // // With other DEs, the X connection will still stay open even after the // X window is closed. For these DEs (e.g. i3) we have to check whether // the WM sent us a DestroyNotifyEvent and close our program: -- cgit v1.2.3 From 02e4a7e5d7f5d91bd42f6537c7ac208aef54f1c2 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 18:26:05 +0100 Subject: drawing basic shapes --- examples/shapes/main.go | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 examples/shapes/main.go (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go new file mode 100644 index 0000000..c03071f --- /dev/null +++ b/examples/shapes/main.go @@ -0,0 +1,109 @@ +// Example shapes shows how to draw basic shapes into a window. +// It can be considered the Go aequivalent of +// https://x.org/releases/X11R7.5/doc/libxcb/tutorial/#drawingprim +// Four points, a single polyline, two line segments, +// two rectangle and two arcs are drawn. +package main + +import ( + "fmt" + + "github.com/jezek/xgb" + "github.com/jezek/xgb/xproto" +) + +func main() { + X, err := xgb.NewConn() + if err != nil { + fmt.Println(err) + return + } + defer X.Close() + + setup := xproto.Setup(X) + screen := setup.DefaultScreen(X) + wid, _ := xproto.NewWindowId(X) + draw := xproto.Drawable(wid) + + // Create the window + xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root, + 0, 0, 500, 500, 8, // X, Y, width, height, *border width* + xproto.WindowClassInputOutput, screen.RootVisual, + xproto.CwBackPixel | xproto.CwEventMask, + []uint32{ screen.WhitePixel, xproto.EventMaskStructureNotify | xproto.EventMaskExposure }) + + // Map the window on the screen + xproto.MapWindow(X, wid) + + // Up to here everything is the same as in the `create-window` example. + // We opened a connection, created and mapped the window. + // Note how this time the border width is set to 8 instead of 0. + // + // But this time we'll be drawing some basic shapes: + + // Create black (foreground) graphic context + foreground, _ := xproto.NewGcontextId(X) + mask := uint32(xproto.GcForeground | xproto.GcGraphicsExposures) + values := []uint32{screen.BlackPixel, 0} + xproto.CreateGC(X, foreground, draw, mask, values) + + points := []xproto.Point{ + {X: 10, Y: 10}, + {X: 20, Y: 10}, + {X: 30, Y: 10}, + {X: 40, Y: 10}, + } + + // A polyline is essientially a line with multiple points: + polyline := []xproto.Point{ + {X: 50, Y: 10}, + {X: 5, Y: 20}, // rest of points are relative + {X: 25, Y: -20}, + {X: 10, Y: 10}, + } + + segments := []xproto.Segment{ + {X1: 100, Y1: 10, X2: 140, Y2: 30}, + {X1: 110, Y1: 25, X2: 130, Y2: 60}, + } + + // Rectangles have a start coordinate (upper left) and width and height. + rectangles := []xproto.Rectangle{ + {X: 10, Y: 50, Width: 40, Height: 20}, + {X: 80, Y: 50, Width: 10, Height: 40}, + } + + arcs := []xproto.Arc{ + {X: 10, Y: 100, Width: 60, Height: 40, Angle1: 0, Angle2: 90 << 6}, + {X: 90, Y: 100, Width: 55, Height: 40, Angle1: 0, Angle2: 270 << 6}, + } + + for { + evt, err := X.WaitForEvent() + switch evt.(type) { + case xproto.ExposeEvent: + /* We draw the points */ + xproto.PolyPoint(X, xproto.CoordModeOrigin, draw, foreground, points) + + /* We draw the polygonal line */ + xproto.PolyLine(X, xproto.CoordModePrevious, draw, foreground, polyline) + + /* We draw the segments */ + xproto.PolySegment(X, draw, foreground, segments) + + /* We draw the rectangles */ + xproto.PolyRectangle(X, draw, foreground, rectangles) + + /* We draw the arcs */ + xproto.PolyArc(X, draw, foreground, arcs) + + case xproto.DestroyNotifyEvent: + return + } + + if err != nil { + fmt.Println(err) + return + } + } +} -- cgit v1.2.3 From 4801f64eb7175e99ff909bfef88abc44a0e7efc1 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 18:41:26 +0100 Subject: draw red rectangle --- examples/shapes/main.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index c03071f..fd8704e 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -41,12 +41,21 @@ func main() { // // But this time we'll be drawing some basic shapes: - // Create black (foreground) graphic context + // First of all we need to create a context to draw with. + // We can get a new context id using NewContextId() + // and have to initialize it with the values: foreground, _ := xproto.NewGcontextId(X) mask := uint32(xproto.GcForeground | xproto.GcGraphicsExposures) values := []uint32{screen.BlackPixel, 0} xproto.CreateGC(X, foreground, draw, mask, values) + // We can also create colored shapes by setting the first value + // when creating the graphics context: + red, _ := xproto.NewGcontextId(X) + // simply reuse the old mask but change the color value: + values[0] = 0xff0000 + xproto.CreateGC(X, red, draw, mask, values) + points := []xproto.Point{ {X: 10, Y: 10}, {X: 20, Y: 10}, @@ -54,12 +63,14 @@ func main() { {X: 40, Y: 10}, } - // A polyline is essientially a line with multiple points: + // A polyline is essientially a line with multiple points. + // The first point is placed absolutely on the screen. + // The other points are relative to the one before them polyline := []xproto.Point{ {X: 50, Y: 10}, - {X: 5, Y: 20}, // rest of points are relative - {X: 25, Y: -20}, - {X: 10, Y: 10}, + {X: 5, Y: 20}, // move 5 to the right, 20 down + {X: 25, Y: -20}, // move 25 to the right, 20 up - notice how this point is level again with the first point + {X: 10, Y: 10}, // move 10 to the right, 10 down } segments := []xproto.Segment{ -- cgit v1.2.3 From 5f9a58e518ef4bf18fa45bdaa956583aadaee356 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 18:51:59 +0100 Subject: draw thick lines --- examples/shapes/main.go | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index fd8704e..0cba361 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -42,20 +42,36 @@ func main() { // But this time we'll be drawing some basic shapes: // First of all we need to create a context to draw with. - // We can get a new context id using NewContextId() - // and have to initialize it with the values: + // The graphics context combines all properties (e.g. color, line width, font, fill style, ...) + // that should be used to draw something. All available properties + // + // These properties can be set by or'ing their keys (xproto.Gc*) + // and adding the value to the end of the values array. + // The order in which the values have to be given corresponds to the order that they defined + // mentioned in `xproto`. + // + // Here we create a new graphics context + // which only has the foreground (color) value set to black: foreground, _ := xproto.NewGcontextId(X) - mask := uint32(xproto.GcForeground | xproto.GcGraphicsExposures) - values := []uint32{screen.BlackPixel, 0} + mask := uint32(xproto.GcForeground) + values := []uint32{ screen.BlackPixel } xproto.CreateGC(X, foreground, draw, mask, values) - // We can also create colored shapes by setting the first value - // when creating the graphics context: + // It is possible to set the foreground value to something different. + // In production, this should use xorg color maps instead for compatibility + // but for demonstration setting the color directly also works: red, _ := xproto.NewGcontextId(X) - // simply reuse the old mask but change the color value: - values[0] = 0xff0000 + mask = uint32(xproto.GcForeground) + values = []uint32{ 0xff0000 } xproto.CreateGC(X, red, draw, mask, values) + // We'll create another graphics context that draws thick lines: + thick, _ := xproto.NewGcontextId(X) + mask = uint32(xproto.GcLineWidth) + values = []uint32{ 10 } + xproto.CreateGC(X, thick, draw, mask, values) + + points := []xproto.Point{ {X: 10, Y: 10}, {X: 20, Y: 10}, @@ -97,13 +113,13 @@ func main() { xproto.PolyPoint(X, xproto.CoordModeOrigin, draw, foreground, points) /* We draw the polygonal line */ - xproto.PolyLine(X, xproto.CoordModePrevious, draw, foreground, polyline) + xproto.PolyLine(X, xproto.CoordModePrevious, draw, red, polyline) /* We draw the segments */ - xproto.PolySegment(X, draw, foreground, segments) + xproto.PolySegment(X, draw, thick, segments) /* We draw the rectangles */ - xproto.PolyRectangle(X, draw, foreground, rectangles) + xproto.PolyRectangle(X, draw, red, rectangles) /* We draw the arcs */ xproto.PolyArc(X, draw, foreground, arcs) -- cgit v1.2.3 From dec3b27246bb3a3ac1b6ec401ec9574611dfbae0 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 19:15:20 +0100 Subject: added more documentation --- examples/shapes/main.go | 77 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 19 deletions(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index 0cba361..0746e2e 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -23,11 +23,11 @@ func main() { setup := xproto.Setup(X) screen := setup.DefaultScreen(X) wid, _ := xproto.NewWindowId(X) - draw := xproto.Drawable(wid) + draw := xproto.Drawable(wid) // for now, we simply draw into the window // Create the window xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root, - 0, 0, 500, 500, 8, // X, Y, width, height, *border width* + 0, 0, 180, 160, 8, // X, Y, width, height, *border width* xproto.WindowClassInputOutput, screen.RootVisual, xproto.CwBackPixel | xproto.CwEventMask, []uint32{ screen.WhitePixel, xproto.EventMaskStructureNotify | xproto.EventMaskExposure }) @@ -59,7 +59,9 @@ func main() { // It is possible to set the foreground value to something different. // In production, this should use xorg color maps instead for compatibility - // but for demonstration setting the color directly also works: + // but for demonstration setting the color directly also works. + // For more information on color maps, see the xcb documentation: + // https://x.org/releases/X11R7.5/doc/libxcb/tutorial/#usecolor red, _ := xproto.NewGcontextId(X) mask = uint32(xproto.GcForeground) values = []uint32{ 0xff0000 } @@ -71,6 +73,24 @@ func main() { values = []uint32{ 10 } xproto.CreateGC(X, thick, draw, mask, values) + // It is even possible to set multiple properties at once. + // Only remember to put the values in the same order as they're + // defined in `xproto`: + // Foreground is defined first, so we also set it's value first. + // LineWidth comes second. + blue, _ := xproto.NewGcontextId(X) + mask = uint32(xproto.GcForeground | xproto.GcLineWidth) + values = []uint32{ 0x0000ff, 4 } + xproto.CreateGC(X, blue, draw, mask, values) + + // Properties of an already created gc can also be changed + // if the original values aren't needed anymore. + // In this case, we will change the line width + // and cap (line corner) style of our foreground context, + // to smooth out the polyline: + mask = uint32(xproto.GcLineWidth | xproto.GcCapStyle) + values = []uint32{ 3, xproto.CapStyleRound } + xproto.ChangeGC(X, foreground, mask, values) points := []xproto.Point{ {X: 10, Y: 10}, @@ -80,8 +100,8 @@ func main() { } // A polyline is essientially a line with multiple points. - // The first point is placed absolutely on the screen. - // The other points are relative to the one before them + // The first point is placed absolutely inside the window, + // while every other point is placed relative to the one before it. polyline := []xproto.Point{ {X: 50, Y: 10}, {X: 5, Y: 20}, // move 5 to the right, 20 down @@ -92,6 +112,7 @@ func main() { segments := []xproto.Segment{ {X1: 100, Y1: 10, X2: 140, Y2: 30}, {X1: 110, Y1: 25, X2: 130, Y2: 60}, + {X1: 0, Y1: 160, X2: 90, Y2: 100}, } // Rectangles have a start coordinate (upper left) and width and height. @@ -100,29 +121,47 @@ func main() { {X: 80, Y: 50, Width: 10, Height: 40}, } + + // Arcs are defined by a top left position (notice where the third line goes to) + // their width and height, a starting and end angle. + // Angles are defined in units of 1/64 of a single degree, + // so we have to multiply the degrees by 64 (or left shift them by 6). arcs := []xproto.Arc{ - {X: 10, Y: 100, Width: 60, Height: 40, Angle1: 0, Angle2: 90 << 6}, - {X: 90, Y: 100, Width: 55, Height: 40, Angle1: 0, Angle2: 270 << 6}, + {X: 10, Y: 100, Width: 60, Height: 40, Angle1: 0 << 6, Angle2: 90 << 6}, + {X: 90, Y: 100, Width: 55, Height: 40, Angle1: 20 << 6, Angle2: 270 << 6}, } for { evt, err := X.WaitForEvent() switch evt.(type) { case xproto.ExposeEvent: - /* We draw the points */ + // Draw the four points we specified earlier. + // Notice how we use the `foreground` context to draw them in black. + // Also notice how even though we changed the line width to 3, + // these still only appear as a single pixel. + // To draw points that are bigger than a single pixel, + // one has to either fill rectangles, circles or polygons. xproto.PolyPoint(X, xproto.CoordModeOrigin, draw, foreground, points) - /* We draw the polygonal line */ - xproto.PolyLine(X, xproto.CoordModePrevious, draw, red, polyline) - - /* We draw the segments */ - xproto.PolySegment(X, draw, thick, segments) - - /* We draw the rectangles */ - xproto.PolyRectangle(X, draw, red, rectangles) - - /* We draw the arcs */ - xproto.PolyArc(X, draw, foreground, arcs) + // Draw the polyline. This time we specified `xproto.CoordModePrevious`, + // which means that every point is placed relatively to the previous. + // If we were to use `xproto.CoordModeOrigin` instead, + // we could specify each point absolutely on the screen. + // It is also possible to `xproto.CoordModePrevious` for drawing points + // which + xproto.PolyLine(X, xproto.CoordModePrevious, draw, foreground, polyline) + + // Draw two lines in red. + xproto.PolySegment(X, draw, red, segments) + + // Draw two thick rectangles. + // The line width only specifies the width of the outline. + // Notice how the second rectangle gets completely filled + // due to the line width. + xproto.PolyRectangle(X, draw, thick, rectangles) + + // Draw the circular arcs in blue. + xproto.PolyArc(X, draw, blue, arcs) case xproto.DestroyNotifyEvent: return -- cgit v1.2.3 From af19f5db5ee6d5d229c788313c8ffdebc0e25c29 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 19:16:45 +0100 Subject: how to fill --- examples/shapes/main.go | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index 0746e2e..0816e61 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -121,6 +121,10 @@ func main() { {X: 80, Y: 50, Width: 10, Height: 40}, } + // This rectangle we will use to demonstrate filling a shape. + rectangles2 := []xproto.Rectangle{ + {X: 150, Y: 50, Width: 20, Height: 60}, + } // Arcs are defined by a top left position (notice where the third line goes to) // their width and height, a starting and end angle. @@ -163,6 +167,9 @@ func main() { // Draw the circular arcs in blue. xproto.PolyArc(X, draw, blue, arcs) + // There's also a fill variant for all drawing commands: + xproto.PolyFillRectangle(X, draw, red, rectangles2) + case xproto.DestroyNotifyEvent: return } -- cgit v1.2.3 From 91a07c7fc7567b3de09706f05c7b0580c6dda56d Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 19:17:46 +0100 Subject: formatting --- examples/shapes/main.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index 0816e61..4025448 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -29,8 +29,8 @@ func main() { xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root, 0, 0, 180, 160, 8, // X, Y, width, height, *border width* xproto.WindowClassInputOutput, screen.RootVisual, - xproto.CwBackPixel | xproto.CwEventMask, - []uint32{ screen.WhitePixel, xproto.EventMaskStructureNotify | xproto.EventMaskExposure }) + xproto.CwBackPixel|xproto.CwEventMask, + []uint32{screen.WhitePixel, xproto.EventMaskStructureNotify | xproto.EventMaskExposure}) // Map the window on the screen xproto.MapWindow(X, wid) @@ -54,7 +54,7 @@ func main() { // which only has the foreground (color) value set to black: foreground, _ := xproto.NewGcontextId(X) mask := uint32(xproto.GcForeground) - values := []uint32{ screen.BlackPixel } + values := []uint32{screen.BlackPixel} xproto.CreateGC(X, foreground, draw, mask, values) // It is possible to set the foreground value to something different. @@ -64,13 +64,13 @@ func main() { // https://x.org/releases/X11R7.5/doc/libxcb/tutorial/#usecolor red, _ := xproto.NewGcontextId(X) mask = uint32(xproto.GcForeground) - values = []uint32{ 0xff0000 } + values = []uint32{0xff0000} xproto.CreateGC(X, red, draw, mask, values) // We'll create another graphics context that draws thick lines: thick, _ := xproto.NewGcontextId(X) mask = uint32(xproto.GcLineWidth) - values = []uint32{ 10 } + values = []uint32{10} xproto.CreateGC(X, thick, draw, mask, values) // It is even possible to set multiple properties at once. @@ -80,7 +80,7 @@ func main() { // LineWidth comes second. blue, _ := xproto.NewGcontextId(X) mask = uint32(xproto.GcForeground | xproto.GcLineWidth) - values = []uint32{ 0x0000ff, 4 } + values = []uint32{0x0000ff, 4} xproto.CreateGC(X, blue, draw, mask, values) // Properties of an already created gc can also be changed @@ -89,7 +89,7 @@ func main() { // and cap (line corner) style of our foreground context, // to smooth out the polyline: mask = uint32(xproto.GcLineWidth | xproto.GcCapStyle) - values = []uint32{ 3, xproto.CapStyleRound } + values = []uint32{3, xproto.CapStyleRound} xproto.ChangeGC(X, foreground, mask, values) points := []xproto.Point{ @@ -104,9 +104,9 @@ func main() { // while every other point is placed relative to the one before it. polyline := []xproto.Point{ {X: 50, Y: 10}, - {X: 5, Y: 20}, // move 5 to the right, 20 down + {X: 5, Y: 20}, // move 5 to the right, 20 down {X: 25, Y: -20}, // move 25 to the right, 20 up - notice how this point is level again with the first point - {X: 10, Y: 10}, // move 10 to the right, 10 down + {X: 10, Y: 10}, // move 10 to the right, 10 down } segments := []xproto.Segment{ @@ -151,8 +151,9 @@ func main() { // which means that every point is placed relatively to the previous. // If we were to use `xproto.CoordModeOrigin` instead, // we could specify each point absolutely on the screen. - // It is also possible to `xproto.CoordModePrevious` for drawing points - // which + // It is also possible to use `xproto.CoordModePrevious` for drawing *points* + // which means that each point would be specified relative to the previous one, + // just as we did with the polyline. xproto.PolyLine(X, xproto.CoordModePrevious, draw, foreground, polyline) // Draw two lines in red. -- cgit v1.2.3 From a4ed6e444609168a8418a4947a4ca9610dcd4772 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 20:06:02 +0100 Subject: found a working font --- examples/shapes/main.go | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index 4025448..1ece00f 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -7,6 +7,7 @@ package main import ( "fmt" + "unicode/utf8" "github.com/jezek/xgb" "github.com/jezek/xgb/xproto" @@ -27,7 +28,7 @@ func main() { // Create the window xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root, - 0, 0, 180, 160, 8, // X, Y, width, height, *border width* + 0, 0, 180, 200, 8, // X, Y, width, height, *border width* xproto.WindowClassInputOutput, screen.RootVisual, xproto.CwBackPixel|xproto.CwEventMask, []uint32{screen.WhitePixel, xproto.EventMaskStructureNotify | xproto.EventMaskExposure}) @@ -92,6 +93,27 @@ func main() { values = []uint32{3, xproto.CapStyleRound} xproto.ChangeGC(X, foreground, mask, values) + // Writing text needs a bit more setup -- we first have + // to open the required font. + // For all available fonts, install and run xfontsel. + font, _ := xproto.NewFontId(X) + fontname := "-gnu-unifont-*-*-*-*-16-*-*-*-*-*-*-*" + err = xproto.OpenFontChecked(X, font, uint16(len(fontname)), fontname).Check() + if err != nil { + fmt.Println("Failed opening the font:", err) + return + } + + // And create a context from it. We simply pass the font's ID to the GcFont property. + textCtx, _ := xproto.NewGcontextId(X) + mask = uint32(xproto.GcForeground | xproto.GcBackground | xproto.GcFont) + values = []uint32{screen.BlackPixel, screen.WhitePixel, uint32(font)} + xproto.CreateGC(X, textCtx, draw, mask, values) + text := convertStringToChar2b("Hellö World!") + + // In the end, writing text is way more comfortable using Xft - it supports TrueType, + // and overall better configuration. + points := []xproto.Point{ {X: 10, Y: 10}, {X: 20, Y: 10}, @@ -171,6 +193,9 @@ func main() { // There's also a fill variant for all drawing commands: xproto.PolyFillRectangle(X, draw, red, rectangles2) + // Draw the text: + xproto.ImageText16(X, byte(len(text)), draw, textCtx, 10, 160, text) + case xproto.DestroyNotifyEvent: return } @@ -181,3 +206,23 @@ func main() { } } } + +func convertStringToChar2b(s string) []xproto.Char2b { + var chars []xproto.Char2b + var p []byte = make([]byte, 2) + + for _, r := range []rune(s) { + utf8.EncodeRune(p, r) + switch utf8.RuneLen(r) { + case 1: + chars = append(chars, xproto.Char2b{Byte1: 0, Byte2: p[0]}) + case 2: + chars = append(chars, xproto.Char2b{Byte1: p[0], Byte2: p[1]}) + fmt.Println(p[0], p[1]) + default: + continue // skip all other characters + } + } + + return chars +} -- cgit v1.2.3 From 65b177c63c9642b41cb2b3377a403796f2f0a8d4 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 20:57:13 +0100 Subject: fixed the umlaut --- examples/shapes/main.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index 1ece00f..0cf7f9c 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -7,7 +7,7 @@ package main import ( "fmt" - "unicode/utf8" + "unicode/utf16" "github.com/jezek/xgb" "github.com/jezek/xgb/xproto" @@ -209,20 +209,23 @@ func main() { func convertStringToChar2b(s string) []xproto.Char2b { var chars []xproto.Char2b - var p []byte = make([]byte, 2) + var p []uint16 for _, r := range []rune(s) { - utf8.EncodeRune(p, r) - switch utf8.RuneLen(r) { - case 1: - chars = append(chars, xproto.Char2b{Byte1: 0, Byte2: p[0]}) - case 2: - chars = append(chars, xproto.Char2b{Byte1: p[0], Byte2: p[1]}) - fmt.Println(p[0], p[1]) - default: - continue // skip all other characters + p = utf16.Encode([]rune{r}) + if len(p) == 1 { + chars = append(chars, convertUint16ToChar2b(p[0])) + } else { + chars = append(chars, xproto.Char2b{Byte1: 0, Byte2: 32}) // add a blank instead } } return chars } + +func convertUint16ToChar2b(u uint16) xproto.Char2b { + return xproto.Char2b{ + Byte1: byte((u & 0xff00) >> 8), + Byte2: byte((u & 0x00ff)), + } +} -- cgit v1.2.3 From 64165cad09ef73cb6acb3852d7650418c36bbfea Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 21:02:41 +0100 Subject: added documentation --- examples/shapes/main.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index 0cf7f9c..cfd873c 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -193,7 +193,11 @@ func main() { // There's also a fill variant for all drawing commands: xproto.PolyFillRectangle(X, draw, red, rectangles2) - // Draw the text: + // Draw the text. Xorg currently knows two ways of specifying text: + // a) the (extended) ASCII encoding using ImageText8(..., []byte) + // b) UTF16 encoding using ImageText16(..., []Char2b) -- Char2b is simply two bytes + // at the bottom of this file, there are two utility functions that help + // convert a go string into an array of Char2b's. xproto.ImageText16(X, byte(len(text)), draw, textCtx, 10, 160, text) case xproto.DestroyNotifyEvent: @@ -207,6 +211,11 @@ func main() { } } +// Char2b is defined as +// Byte1 byte +// Byte2 byte +// and is used as a utf16 character. +// This function takes a string and converts each rune into a char2b. func convertStringToChar2b(s string) []xproto.Char2b { var chars []xproto.Char2b var p []uint16 @@ -216,13 +225,18 @@ func convertStringToChar2b(s string) []xproto.Char2b { if len(p) == 1 { chars = append(chars, convertUint16ToChar2b(p[0])) } else { - chars = append(chars, xproto.Char2b{Byte1: 0, Byte2: 32}) // add a blank instead + // If the utf16 representation is larger than 2 bytes + // we can not use it and insert a blank instead: + chars = append(chars, xproto.Char2b{Byte1: 0, Byte2: 32}) } } return chars } +// convertUint16ToChar2b converts a uint16 (which is basically two bytes) +// into a Char2b by using the higher 8 bits of u as Byte1 +// and the lower 8 bits of u as Byte2. func convertUint16ToChar2b(u uint16) xproto.Char2b { return xproto.Char2b{ Byte1: byte((u & 0xff00) >> 8), -- cgit v1.2.3 From e58a1961dd3bf91541c911216a626f6be10e9dd1 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sat, 6 Mar 2021 21:08:19 +0100 Subject: close font after usage --- examples/shapes/main.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index cfd873c..7a74da2 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -109,9 +109,12 @@ func main() { mask = uint32(xproto.GcForeground | xproto.GcBackground | xproto.GcFont) values = []uint32{screen.BlackPixel, screen.WhitePixel, uint32(font)} xproto.CreateGC(X, textCtx, draw, mask, values) - text := convertStringToChar2b("Hellö World!") + text := convertStringToChar2b("Hellö World!") // Unicode capable! - // In the end, writing text is way more comfortable using Xft - it supports TrueType, + // Close the font handle: + xproto.CloseFont(X, font) + + // After all, writing text is way more comfortable using Xft - it supports TrueType, // and overall better configuration. points := []xproto.Point{ -- cgit v1.2.3 From 3577bf0f9657fa3b9edd77a71b15b8a09634461a Mon Sep 17 00:00:00 2001 From: Lenni Date: Sun, 7 Mar 2021 09:12:38 +0100 Subject: removed shapes from this PR --- examples/shapes/main.go | 248 ------------------------------------------------ 1 file changed, 248 deletions(-) delete mode 100644 examples/shapes/main.go (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go deleted file mode 100644 index 7a74da2..0000000 --- a/examples/shapes/main.go +++ /dev/null @@ -1,248 +0,0 @@ -// Example shapes shows how to draw basic shapes into a window. -// It can be considered the Go aequivalent of -// https://x.org/releases/X11R7.5/doc/libxcb/tutorial/#drawingprim -// Four points, a single polyline, two line segments, -// two rectangle and two arcs are drawn. -package main - -import ( - "fmt" - "unicode/utf16" - - "github.com/jezek/xgb" - "github.com/jezek/xgb/xproto" -) - -func main() { - X, err := xgb.NewConn() - if err != nil { - fmt.Println(err) - return - } - defer X.Close() - - setup := xproto.Setup(X) - screen := setup.DefaultScreen(X) - wid, _ := xproto.NewWindowId(X) - draw := xproto.Drawable(wid) // for now, we simply draw into the window - - // Create the window - xproto.CreateWindow(X, screen.RootDepth, wid, screen.Root, - 0, 0, 180, 200, 8, // X, Y, width, height, *border width* - xproto.WindowClassInputOutput, screen.RootVisual, - xproto.CwBackPixel|xproto.CwEventMask, - []uint32{screen.WhitePixel, xproto.EventMaskStructureNotify | xproto.EventMaskExposure}) - - // Map the window on the screen - xproto.MapWindow(X, wid) - - // Up to here everything is the same as in the `create-window` example. - // We opened a connection, created and mapped the window. - // Note how this time the border width is set to 8 instead of 0. - // - // But this time we'll be drawing some basic shapes: - - // First of all we need to create a context to draw with. - // The graphics context combines all properties (e.g. color, line width, font, fill style, ...) - // that should be used to draw something. All available properties - // - // These properties can be set by or'ing their keys (xproto.Gc*) - // and adding the value to the end of the values array. - // The order in which the values have to be given corresponds to the order that they defined - // mentioned in `xproto`. - // - // Here we create a new graphics context - // which only has the foreground (color) value set to black: - foreground, _ := xproto.NewGcontextId(X) - mask := uint32(xproto.GcForeground) - values := []uint32{screen.BlackPixel} - xproto.CreateGC(X, foreground, draw, mask, values) - - // It is possible to set the foreground value to something different. - // In production, this should use xorg color maps instead for compatibility - // but for demonstration setting the color directly also works. - // For more information on color maps, see the xcb documentation: - // https://x.org/releases/X11R7.5/doc/libxcb/tutorial/#usecolor - red, _ := xproto.NewGcontextId(X) - mask = uint32(xproto.GcForeground) - values = []uint32{0xff0000} - xproto.CreateGC(X, red, draw, mask, values) - - // We'll create another graphics context that draws thick lines: - thick, _ := xproto.NewGcontextId(X) - mask = uint32(xproto.GcLineWidth) - values = []uint32{10} - xproto.CreateGC(X, thick, draw, mask, values) - - // It is even possible to set multiple properties at once. - // Only remember to put the values in the same order as they're - // defined in `xproto`: - // Foreground is defined first, so we also set it's value first. - // LineWidth comes second. - blue, _ := xproto.NewGcontextId(X) - mask = uint32(xproto.GcForeground | xproto.GcLineWidth) - values = []uint32{0x0000ff, 4} - xproto.CreateGC(X, blue, draw, mask, values) - - // Properties of an already created gc can also be changed - // if the original values aren't needed anymore. - // In this case, we will change the line width - // and cap (line corner) style of our foreground context, - // to smooth out the polyline: - mask = uint32(xproto.GcLineWidth | xproto.GcCapStyle) - values = []uint32{3, xproto.CapStyleRound} - xproto.ChangeGC(X, foreground, mask, values) - - // Writing text needs a bit more setup -- we first have - // to open the required font. - // For all available fonts, install and run xfontsel. - font, _ := xproto.NewFontId(X) - fontname := "-gnu-unifont-*-*-*-*-16-*-*-*-*-*-*-*" - err = xproto.OpenFontChecked(X, font, uint16(len(fontname)), fontname).Check() - if err != nil { - fmt.Println("Failed opening the font:", err) - return - } - - // And create a context from it. We simply pass the font's ID to the GcFont property. - textCtx, _ := xproto.NewGcontextId(X) - mask = uint32(xproto.GcForeground | xproto.GcBackground | xproto.GcFont) - values = []uint32{screen.BlackPixel, screen.WhitePixel, uint32(font)} - xproto.CreateGC(X, textCtx, draw, mask, values) - text := convertStringToChar2b("Hellö World!") // Unicode capable! - - // Close the font handle: - xproto.CloseFont(X, font) - - // After all, writing text is way more comfortable using Xft - it supports TrueType, - // and overall better configuration. - - points := []xproto.Point{ - {X: 10, Y: 10}, - {X: 20, Y: 10}, - {X: 30, Y: 10}, - {X: 40, Y: 10}, - } - - // A polyline is essientially a line with multiple points. - // The first point is placed absolutely inside the window, - // while every other point is placed relative to the one before it. - polyline := []xproto.Point{ - {X: 50, Y: 10}, - {X: 5, Y: 20}, // move 5 to the right, 20 down - {X: 25, Y: -20}, // move 25 to the right, 20 up - notice how this point is level again with the first point - {X: 10, Y: 10}, // move 10 to the right, 10 down - } - - segments := []xproto.Segment{ - {X1: 100, Y1: 10, X2: 140, Y2: 30}, - {X1: 110, Y1: 25, X2: 130, Y2: 60}, - {X1: 0, Y1: 160, X2: 90, Y2: 100}, - } - - // Rectangles have a start coordinate (upper left) and width and height. - rectangles := []xproto.Rectangle{ - {X: 10, Y: 50, Width: 40, Height: 20}, - {X: 80, Y: 50, Width: 10, Height: 40}, - } - - // This rectangle we will use to demonstrate filling a shape. - rectangles2 := []xproto.Rectangle{ - {X: 150, Y: 50, Width: 20, Height: 60}, - } - - // Arcs are defined by a top left position (notice where the third line goes to) - // their width and height, a starting and end angle. - // Angles are defined in units of 1/64 of a single degree, - // so we have to multiply the degrees by 64 (or left shift them by 6). - arcs := []xproto.Arc{ - {X: 10, Y: 100, Width: 60, Height: 40, Angle1: 0 << 6, Angle2: 90 << 6}, - {X: 90, Y: 100, Width: 55, Height: 40, Angle1: 20 << 6, Angle2: 270 << 6}, - } - - for { - evt, err := X.WaitForEvent() - switch evt.(type) { - case xproto.ExposeEvent: - // Draw the four points we specified earlier. - // Notice how we use the `foreground` context to draw them in black. - // Also notice how even though we changed the line width to 3, - // these still only appear as a single pixel. - // To draw points that are bigger than a single pixel, - // one has to either fill rectangles, circles or polygons. - xproto.PolyPoint(X, xproto.CoordModeOrigin, draw, foreground, points) - - // Draw the polyline. This time we specified `xproto.CoordModePrevious`, - // which means that every point is placed relatively to the previous. - // If we were to use `xproto.CoordModeOrigin` instead, - // we could specify each point absolutely on the screen. - // It is also possible to use `xproto.CoordModePrevious` for drawing *points* - // which means that each point would be specified relative to the previous one, - // just as we did with the polyline. - xproto.PolyLine(X, xproto.CoordModePrevious, draw, foreground, polyline) - - // Draw two lines in red. - xproto.PolySegment(X, draw, red, segments) - - // Draw two thick rectangles. - // The line width only specifies the width of the outline. - // Notice how the second rectangle gets completely filled - // due to the line width. - xproto.PolyRectangle(X, draw, thick, rectangles) - - // Draw the circular arcs in blue. - xproto.PolyArc(X, draw, blue, arcs) - - // There's also a fill variant for all drawing commands: - xproto.PolyFillRectangle(X, draw, red, rectangles2) - - // Draw the text. Xorg currently knows two ways of specifying text: - // a) the (extended) ASCII encoding using ImageText8(..., []byte) - // b) UTF16 encoding using ImageText16(..., []Char2b) -- Char2b is simply two bytes - // at the bottom of this file, there are two utility functions that help - // convert a go string into an array of Char2b's. - xproto.ImageText16(X, byte(len(text)), draw, textCtx, 10, 160, text) - - case xproto.DestroyNotifyEvent: - return - } - - if err != nil { - fmt.Println(err) - return - } - } -} - -// Char2b is defined as -// Byte1 byte -// Byte2 byte -// and is used as a utf16 character. -// This function takes a string and converts each rune into a char2b. -func convertStringToChar2b(s string) []xproto.Char2b { - var chars []xproto.Char2b - var p []uint16 - - for _, r := range []rune(s) { - p = utf16.Encode([]rune{r}) - if len(p) == 1 { - chars = append(chars, convertUint16ToChar2b(p[0])) - } else { - // If the utf16 representation is larger than 2 bytes - // we can not use it and insert a blank instead: - chars = append(chars, xproto.Char2b{Byte1: 0, Byte2: 32}) - } - } - - return chars -} - -// convertUint16ToChar2b converts a uint16 (which is basically two bytes) -// into a Char2b by using the higher 8 bits of u as Byte1 -// and the lower 8 bits of u as Byte2. -func convertUint16ToChar2b(u uint16) xproto.Char2b { - return xproto.Char2b{ - Byte1: byte((u & 0xff00) >> 8), - Byte2: byte((u & 0x00ff)), - } -} -- cgit v1.2.3 From 257276cd70a8423fbe4283c6d7a6b99a0c3d23eb Mon Sep 17 00:00:00 2001 From: Lenni Date: Sun, 7 Mar 2021 09:23:33 +0100 Subject: minor fixes --- examples/create-window/main.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/create-window/main.go b/examples/create-window/main.go index ef7d98a..9d1784c 100644 --- a/examples/create-window/main.go +++ b/examples/create-window/main.go @@ -113,7 +113,7 @@ func main() { // and reacts to it accordingly. All events are defined in the xproto subpackage. switch ev.(type) { case xproto.KeyPressEvent: - // See https://pkg.go.dev/github.com/jezek/xgb@v0.0.0-20210121230032-cec22bda1ce1/xproto#KeyPressEvent + // See https://pkg.go.dev/github.com/jezek/xgb/xproto#KeyPressEvent // for documentation about a key press event. kpe := ev.(xproto.KeyPressEvent) fmt.Printf("Key pressed: %d", kpe.Detail) @@ -129,7 +129,7 @@ func main() { // desktop environment). // // If that's the case for your environment, closing this example's window - // will also call the underlying Go program (because closing the X + // will also close the underlying Go program (because closing the X // connection gives a nil event and EOF error). // // Consider how a single application might have multiple windows @@ -137,7 +137,11 @@ func main() { // // With other DEs, the X connection will still stay open even after the // X window is closed. For these DEs (e.g. i3) we have to check whether - // the WM sent us a DestroyNotifyEvent and close our program: + // the WM sent us a DestroyNotifyEvent and close our program. + // + // For more information about closing windows while maintaining + // the X connection see + // https://github.com/jezek/xgbutil/blob/master/_examples/graceful-window-close/main.go return } } -- cgit v1.2.3 From c3652e28fc56c658e9011742567e52ee2f22ed98 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sun, 7 Mar 2021 09:26:04 +0100 Subject: rephrased weird comment --- examples/create-window/main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/create-window/main.go b/examples/create-window/main.go index 9d1784c..c096a91 100644 --- a/examples/create-window/main.go +++ b/examples/create-window/main.go @@ -107,10 +107,10 @@ func main() { } // This is how accepting events work: - // The application checks what event we got - // (the event must be registered using either xproto.CreateWindow (see l.35) or - // xproto.ChangeWindowAttributes (see l.50)). - // and reacts to it accordingly. All events are defined in the xproto subpackage. + // The application checks what event we got and reacts to it + // accordingly. All events are defined in the xproto subpackage. + // To receive events, we have to first register it using + // either xproto.CreateWindow or xproto.ChangeWindowAttributes. switch ev.(type) { case xproto.KeyPressEvent: // See https://pkg.go.dev/github.com/jezek/xgb/xproto#KeyPressEvent -- cgit v1.2.3 From 06eab223430e738079eb644911256671a319980e Mon Sep 17 00:00:00 2001 From: jEzEk Date: Sun, 7 Mar 2021 15:13:16 +0100 Subject: Add missing "\n" at the end of fmt.Printf --- examples/create-window/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/create-window/main.go b/examples/create-window/main.go index c096a91..a637d0e 100644 --- a/examples/create-window/main.go +++ b/examples/create-window/main.go @@ -116,7 +116,7 @@ func main() { // See https://pkg.go.dev/github.com/jezek/xgb/xproto#KeyPressEvent // for documentation about a key press event. kpe := ev.(xproto.KeyPressEvent) - fmt.Printf("Key pressed: %d", kpe.Detail) + fmt.Printf("Key pressed: %d\n", kpe.Detail) // The Detail value depends on the keyboard layout, // for QWERTY, q is #24. if kpe.Detail == 24 { -- cgit v1.2.3