From 45de451c0cbda69310da95f005f53cf232467ebe Mon Sep 17 00:00:00 2001 From: Lenni Date: Sun, 7 Mar 2021 16:11:29 +0100 Subject: shapes example --- examples/shapes/main.go | 248 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 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..df733e1 --- /dev/null +++ b/examples/shapes/main.go @@ -0,0 +1,248 @@ +// The shapes example 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 d8c044d9e986a90d017567a2be538801b98a9243 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sun, 7 Mar 2021 16:12:34 +0100 Subject: more info --- examples/shapes/main.go | 2 ++ 1 file changed, 2 insertions(+) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index df733e1..d2f8132 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -3,6 +3,8 @@ // 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. +// In addition to this, we will also write some text +// and fill a rectangle. package main import ( -- cgit v1.2.3 From 84e3375e08953a6b9f5b42b6358c34be7d701338 Mon Sep 17 00:00:00 2001 From: Lenni Date: Sun, 7 Mar 2021 16:19:31 +0100 Subject: reworded a weird comment --- examples/shapes/main.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index d2f8132..ffc911d 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -200,8 +200,9 @@ func main() { // 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 + // b) UTF16 encoding using ImageText16(..., []Char2b) -- Char2b is + // a structure consisting of two bytes. + // At the bottom of this example, 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) -- cgit v1.2.3 From 6f5d8a2bd52dc6a4e877f0b3856ff2db429edf54 Mon Sep 17 00:00:00 2001 From: jEzEk Date: Tue, 9 Mar 2021 16:07:26 +0100 Subject: Fix typos and reword comment --- examples/shapes/main.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index ffc911d..2686d2e 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -1,8 +1,8 @@ // The shapes example shows how to draw basic shapes into a window. -// It can be considered the Go aequivalent of +// It can be considered the Go equivalent 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. +// two rectangles and two arcs are drawn. // In addition to this, we will also write some text // and fill a rectangle. package main @@ -40,10 +40,9 @@ func main() { // Up to here everything is the same as in the `create-window` example. // We opened a connection, created and mapped the window. + // But this time we'll be drawing some basic shapes. // 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 @@ -126,7 +125,7 @@ func main() { {X: 40, Y: 10}, } - // A polyline is essientially a line with multiple points. + // A polyline is essentially 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{ -- cgit v1.2.3 From cf4da220d97996f1eaa893432ee74f546ab40fde Mon Sep 17 00:00:00 2001 From: Lenni Date: Tue, 9 Mar 2021 17:03:11 +0100 Subject: error handling --- examples/shapes/main.go | 66 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 14 deletions(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index 2686d2e..3f2390c 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -18,14 +18,19 @@ import ( func main() { X, err := xgb.NewConn() if err != nil { - fmt.Println(err) + fmt.Println("error connecting to X:", err) return } defer X.Close() setup := xproto.Setup(X) screen := setup.DefaultScreen(X) - wid, _ := xproto.NewWindowId(X) + wid, err := xproto.NewWindowId(X) + if err != nil { + fmt.Println("error creating window id:", err) + return + } + draw := xproto.Drawable(wid) // for now, we simply draw into the window // Create the window @@ -54,7 +59,12 @@ func main() { // // Here we create a new graphics context // which only has the foreground (color) value set to black: - foreground, _ := xproto.NewGcontextId(X) + foreground, err := xproto.NewGcontextId(X) + if err != nil { + fmt.Println("error creating foreground context:", err) + return + } + mask := uint32(xproto.GcForeground) values := []uint32{screen.BlackPixel} xproto.CreateGC(X, foreground, draw, mask, values) @@ -64,13 +74,23 @@ func main() { // 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) + red, err := xproto.NewGcontextId(X) + if err != nil { + fmt.Println("error creating red context:", err) + return + } + 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) + thick, err := xproto.NewGcontextId(X) + if err != nil { + fmt.Println("error creating thick context:", err) + return + } + mask = uint32(xproto.GcLineWidth) values = []uint32{10} xproto.CreateGC(X, thick, draw, mask, values) @@ -80,7 +100,12 @@ func main() { // defined in `xproto`: // Foreground is defined first, so we also set it's value first. // LineWidth comes second. - blue, _ := xproto.NewGcontextId(X) + blue, err := xproto.NewGcontextId(X) + if err != nil { + fmt.Println("error creating blue context:", err) + return + } + mask = uint32(xproto.GcForeground | xproto.GcLineWidth) values = []uint32{0x0000ff, 4} xproto.CreateGC(X, blue, draw, mask, values) @@ -97,16 +122,26 @@ func main() { // 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) + font, err := xproto.NewFontId(X) + if err != nil { + fmt.Println("error creating font id:", err) + return + } + fontname := "-gnu-unifont-*-*-*-*-16-*-*-*-*-*-*-*" err = xproto.OpenFontChecked(X, font, uint16(len(fontname)), fontname).Check() if err != nil { - fmt.Println("Failed opening the font:", err) + 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) + textCtx, err := xproto.NewGcontextId(X) + if err != nil { + fmt.Println("error creating text context:", err) + return + } + mask = uint32(xproto.GcForeground | xproto.GcBackground | xproto.GcFont) values = []uint32{screen.BlackPixel, screen.WhitePixel, uint32(font)} xproto.CreateGC(X, textCtx, draw, mask, values) @@ -163,6 +198,14 @@ func main() { for { evt, err := X.WaitForEvent() + + if err != nil { + fmt.Println("error reading event:", err) + return + } else if evt == nil && err == nil { + return + } + switch evt.(type) { case xproto.ExposeEvent: // Draw the four points we specified earlier. @@ -208,11 +251,6 @@ func main() { case xproto.DestroyNotifyEvent: return } - - if err != nil { - fmt.Println(err) - return - } } } -- cgit v1.2.3 From cee3f874a09094a7a4ed493adf80fc7976746727 Mon Sep 17 00:00:00 2001 From: Lenni Date: Tue, 9 Mar 2021 17:55:27 +0100 Subject: load unicode font --- 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 3f2390c..5137f0b 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -121,14 +121,28 @@ func main() { // Writing text needs a bit more setup -- we first have // to open the required font. - // For all available fonts, install and run xfontsel. font, err := xproto.NewFontId(X) if err != nil { fmt.Println("error creating font id:", err) return } - fontname := "-gnu-unifont-*-*-*-*-16-*-*-*-*-*-*-*" + // The font identifier that has to be passed to X for opening the font + // sets all font properties: + // publisher-family-weight-slant-width-adstyl-pxlsz-ptSz-resx-resy-spc-avgWidth-registry-encoding + // For all available fonts, install and run xfontsel. + // + // To load any available font, set all fields to an asterisk. + // To specify a font, set one or multiple fields. + // This can also be seen in xfontsel -- initially every field is set to *, + // however, the more fields are set, the fewer fonts match. + // + // Using a specific font (e.g. Gnu Unifont) can be as easy as + // "-gnu-unifont-*-*-*-*-16-*-*-*-*-*-*-*" + // + // For this example we simply want to load any font of size 14 which is capable + // of displaying Unicode characters: + fontname := "-*-*-*-*-*-*-14-*-*-*-*-*-iso10646-1" err = xproto.OpenFontChecked(X, font, uint16(len(fontname)), fontname).Check() if err != nil { fmt.Println("failed opening the font:", err) -- cgit v1.2.3 From 6bce073f59991e537566a658829f7526aa4bafa0 Mon Sep 17 00:00:00 2001 From: Lenni Date: Wed, 10 Mar 2021 20:38:43 +0100 Subject: minor fix --- examples/shapes/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index 5137f0b..4bc3ee3 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -216,7 +216,7 @@ func main() { if err != nil { fmt.Println("error reading event:", err) return - } else if evt == nil && err == nil { + } else if evt == nil { return } -- cgit v1.2.3 From b0287db741413703bd3db5d25648710660ffe088 Mon Sep 17 00:00:00 2001 From: Lenni Date: Wed, 10 Mar 2021 20:45:26 +0100 Subject: use fixed font --- examples/shapes/main.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/shapes/main.go b/examples/shapes/main.go index 4bc3ee3..aecc21f 100644 --- a/examples/shapes/main.go +++ b/examples/shapes/main.go @@ -140,9 +140,13 @@ func main() { // Using a specific font (e.g. Gnu Unifont) can be as easy as // "-gnu-unifont-*-*-*-*-16-*-*-*-*-*-*-*" // - // For this example we simply want to load any font of size 14 which is capable - // of displaying Unicode characters: - fontname := "-*-*-*-*-*-*-14-*-*-*-*-*-iso10646-1" + // To load any font that is encoded for usage + // with Unicode characters, one would use + // fontname := "-*-*-*-*-*-*-14-*-*-*-*-*-iso10646-1" + // + // For now, we'll simply stick with the fixed font which is available + // to every X session: + fontname := "-*-fixed-*-*-*-*-14-*-*-*-*-*-*-*" err = xproto.OpenFontChecked(X, font, uint16(len(fontname)), fontname).Check() if err != nil { fmt.Println("failed opening the font:", err) -- cgit v1.2.3