Window creation/X11: Difference between revisions
(→Demonstration script: Add comments) |
(→{{header|Tcl}}: Rewrite to be clearer about how different levels of interface can exist) |
||
Line 196: | Line 196: | ||
Tcl does not come with a low-level connection to the X protocol, as it has long been distributed with [[Tk]] which offers a much higher-level interface (and which is portable to other platforms too). This means that the interface has to be crafted directly. This can be done with either [http://www.swig.org/ SWIG] or [[critcl]]. This example shows how to do it the latter way: |
Tcl does not come with a low-level connection to the X protocol, as it has long been distributed with [[Tk]] which offers a much higher-level interface (and which is portable to other platforms too). This means that the interface has to be crafted directly. This can be done with either [http://www.swig.org/ SWIG] or [[critcl]]. This example shows how to do it the latter way: |
||
===Low level interface |
===Low level interface=== |
||
{{libheader|critcl}} |
{{libheader|critcl}} |
||
<lang tcl>package provide xlib 1 |
<lang tcl>package provide xlib 1 |
||
Line 204: | Line 204: | ||
critcl::ccode { |
critcl::ccode { |
||
#include <X11/Xlib.h> |
#include <X11/Xlib.h> |
||
Display *d; |
static Display *d; |
||
GC gc; |
static GC gc; |
||
} |
} |
||
# Display connection functions |
|||
critcl::cproc XOpenDisplay {Tcl_Interp* interp char* name} ok { |
critcl::cproc XOpenDisplay {Tcl_Interp* interp char* name} ok { |
||
d = XOpenDisplay(name[0] ? name : NULL); |
d = XOpenDisplay(name[0] ? name : NULL); |
||
Line 217: | Line 218: | ||
return TCL_OK; |
return TCL_OK; |
||
} |
} |
||
critcl::cproc XCloseDisplay {} void { |
critcl::cproc XCloseDisplay {} void { |
||
XCloseDisplay(d); |
XCloseDisplay(d); |
||
} |
} |
||
# Basic window functions |
|||
critcl::cproc XCreateSimpleWindow { |
critcl::cproc XCreateSimpleWindow { |
||
int x int y int width int height int events |
int x int y int width int height int events |
||
Line 231: | Line 232: | ||
return (int) w; |
return (int) w; |
||
} |
} |
||
critcl::cproc XDestroyWindow {int w} void { |
|||
XDestroyWindow(d, (Window) w); |
|||
⚫ | |||
critcl::cproc XMapWindow {int w} void { |
critcl::cproc XMapWindow {int w} void { |
||
XMapWindow(d, (Window) w); |
XMapWindow(d, (Window) w); |
||
Line 239: | Line 242: | ||
} |
} |
||
# Event receiver |
|||
critcl::cproc XNextEvent {Tcl_Interp* interp} char* { |
critcl::cproc XNextEvent {Tcl_Interp* interp} char* { |
||
XEvent e; |
XEvent e; |
||
Line 250: | Line 254: | ||
} |
} |
||
# Painting functions |
|||
critcl::cproc XFillRectangle {int w int x int y int width int height} void { |
critcl::cproc XFillRectangle {int w int x int y int width int height} void { |
||
XFillRectangle(d, (Window)w, gc, x, y, width, height); |
XFillRectangle(d, (Window)w, gc, x, y, width, height); |
||
} |
} |
||
critcl::cproc XDrawString {int w int x int y Tcl_Obj* msg} void { |
critcl::cproc XDrawString {int w int x int y Tcl_Obj* msg} void { |
||
int len; |
int len; |
||
Line 260: | Line 264: | ||
}</lang> |
}</lang> |
||
Note that this only does enough for this demo. A full adaptation is too long for RosettaCode... |
Note that this only does enough for this demo. A full adaptation is too long for RosettaCode... |
||
⚫ | |||
This could then be used like this: |
|||
⚫ | |||
<lang tcl>package require |
<lang tcl>package require xlib |
||
⚫ | |||
XOpenDisplay {} |
|||
⚫ | |||
XMapWindow $w |
|||
while {[XNextEvent] == "expose"} { |
|||
XFillRectangle $w 20 20 10 10 |
|||
XDrawString $w 10 50 "Hello, World!" |
|||
} |
|||
XDestroyWindow $w |
|||
XCloseDisplay</lang> |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
namespace eval ::x { |
namespace eval ::x { |
||
Line 277: | Line 294: | ||
proc display {script} { |
proc display {script} { |
||
XOpenDisplay {} |
|||
catch {uplevel 1 $script} msg opts |
catch {uplevel 1 $script} msg opts |
||
XCloseDisplay |
|||
CloseDisplay |
|||
return -options $opts $msg |
return -options $opts $msg |
||
} |
} |
||
Line 285: | Line 302: | ||
upvar 1 $var v |
upvar 1 $var v |
||
while 1 { |
while 1 { |
||
set v [ |
set v [XNextEvent] |
||
uplevel 1 [list switch $v $handlers] |
uplevel 1 [list switch $v $handlers] |
||
} |
} |
||
} |
} |
||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
oo::class create window { |
|||
⚫ | |||
⚫ | |||
rename ::XOpenDisplay ::x::OpenDisplay |
|||
⚫ | |||
rename ::XCloseDisplay ::x::CloseDisplay |
|||
⚫ | |||
⚫ | |||
variable ::x::mask |
|||
rename ::XMapWindow ::x::map |
|||
⚫ | |||
rename ::XUnmapWindow ::x::unmap |
|||
set w [XCreateSimpleWindow $x $y $width $height $m] |
|||
rename ::XNextEvent ::x::WaitForEvent |
|||
} |
|||
rename ::XFillRectangle ::x::fillrect |
|||
method map {} { |
|||
rename ::XDrawString ::x::text</lang> |
|||
XMapWindow $w |
|||
===Demonstration script=== |
|||
} |
|||
method unmap {} { |
|||
XUnmapWindow $w |
|||
} |
|||
⚫ | |||
XFillRectangle $w $x $y $width $height |
|||
} |
|||
method text {x y string} { |
|||
XDrawString $w $x $y $string |
|||
} |
|||
destructor { |
|||
XDestroyWindow $w |
|||
} |
|||
⚫ | |||
}</lang> |
|||
This script puts the pieces together to carry out the details of the task. |
This script puts the pieces together to carry out the details of the task. |
||
<lang tcl> |
<lang tcl>package require x11 |
||
package require x |
|||
# With a display connection open, create and map a window |
# With a display connection open, create and map a window |
||
x display { |
x display { |
||
set w [x window 10 10 100 100 KeyPress] |
set w [x window new 10 10 100 100 KeyPress] |
||
$w map |
|||
x eventloop e { |
x eventloop e { |
||
expose { |
expose { |
||
# Paint the window |
# Paint the window |
||
$w fill 20 20 10 10 |
|||
$w text 10 50 "Hello, World!" |
|||
} |
} |
||
key { |
key { |
||
Line 327: | Line 351: | ||
} |
} |
||
} |
} |
||
$w destroy |
|||
}</lang> |
}</lang> |
||
Improving this by adding more sophisticated event handling and more window methods is left as an exercise. |
Revision as of 23:07, 21 August 2009
You are encouraged to solve this task according to the task description, using any language you may know.
Create a simple X11 application, using an X11 protocol library such as Xlib, that draws a box and "Hello World" in a window
ALGOL 68
Using the X11 & plotutil libraries is not part of any of the original UNESCO/IFIP ALGOL 68 reports. As at December 2008 only ALGOL 68G comes with these built in.
FILE window; draw device (window, "X", "600x400"); open (window, "Hello, World!", stand draw channel); draw erase (window); draw move (window, 0.25, 0.5); draw colour (window, 1, 0, 0); draw text (window, "c", "c", "hello world"); draw show (window); VOID (read char); close (window)
C
Compile with:
- gcc -L/usr/X11R6/lib -lX11 hello-x.c -o hello-x
<lang c>#include <X11/Xlib.h>
- include <stdio.h>
- include <stdlib.h>
- include <string.h>
int main(void) {
Display *d; Window w; XEvent e; char *msg = "Hello, World!"; int s;
d = XOpenDisplay(NULL); if (d == NULL) { fprintf(stderr, "Cannot open display\n"); exit(1); }
s = DefaultScreen(d); w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1, BlackPixel(d, s), WhitePixel(d, s)); XSelectInput(d, w, ExposureMask | KeyPressMask); XMapWindow(d, w);
while (1) { XNextEvent(d, &e); if (e.type == Expose) { XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10); XDrawString(d, w, DefaultGC(d, s), 10, 50, msg, strlen(msg)); } if (e.type == KeyPress) break; }
XCloseDisplay(d); return 0;
} </lang>
Common Lisp
This example uses CLX, which is the de facto standard X11 library for Common Lisp. CLX is not a binding to Xlib; it is a Lisp library implementing the X11 protocol.
<lang lisp>;;; Single-file/interactive setup; large applications should define an ASDF system instead (cl:require :asdf) (asdf:operate 'asdf:load-op :clx) (cl:defpackage #:rc-xlib-window
(:use #:cl #:xlib))
(cl:in-package #:rc-xlib-window)
(let ((display (open-default-display)))
(unwind-protect (let* ((window (create-window :parent (screen-root (display-default-screen display)) :x 10 :y 10 :width 100 :height 100 :event-mask '(:exposure :key-press))) (gc (create-gcontext :drawable window))) (map-window window) (event-case (display :discard-p t) (exposure () (draw-rectangle window gc 20 20 10 10 t) (draw-glyphs window gc 10 40 "Hello, World!") nil #| continue receiving events |#) (key-press () t #| non-nil result signals event-case to exit |#)))) (close-display display))</lang>
OCaml
execute as a script with:
ocaml -I +Xlib Xlib.cma script.ml
or compile to native code:
ocamlopt -I +Xlib Xlib.cmxa prog.ml -o prog
or to make a standalone script add these lines at the beginning of the file:
#!/usr/bin/env ocaml #directory "+Xlib" #load "Xlib.cma"
<lang ocaml>open Xlib
let () =
let d = xOpenDisplay "" in let s = xDefaultScreen d in let w = xCreateSimpleWindow d (xRootWindow d s) 10 10 100 100 1 (xBlackPixel d s) (xWhitePixel d s) in xSelectInput d w [ExposureMask; KeyPressMask]; xMapWindow d w;
let msg = "Hello, World!" in
let rec main_loop() = match xEventType(xNextEventFun d) with | Expose -> xFillRectangle d w (xDefaultGC d s) 20 20 10 10; xDrawString d w (xDefaultGC d s) 10 50 msg; main_loop() | KeyPress -> () (* exit main loop *) | _ -> main_loop() in main_loop(); xCloseDisplay d;
- </lang>
Python
Download Python X library from http://sourceforge.net/projects/python-xlib/ . python-xlib is a pure python library therefore the example should work anywhere where python does. Run:
- python xlib_hello_world.py
<lang python>from Xlib import X, display
class Window:
def __init__(self, display, msg): self.display = display self.msg = msg self.screen = self.display.screen() self.window = self.screen.root.create_window( 10, 10, 100, 100, 1, self.screen.root_depth, background_pixel=self.screen.white_pixel, event_mask=X.ExposureMask | X.KeyPressMask, ) self.gc = self.window.create_gc( foreground = self.screen.black_pixel, background = self.screen.white_pixel, )
self.window.map()
def loop(self): while True: e = self.display.next_event() if e.type == X.Expose: self.window.fill_rectangle(self.gc, 20, 20, 10, 10) self.window.draw_text(self.gc, 10, 50, self.msg) elif e.type == X.KeyPress: raise SystemExit
if __name__ == "__main__":
Window(display.Display(), "Hello, World!").loop()
</lang>
Tcl
Tcl does not come with a low-level connection to the X protocol, as it has long been distributed with Tk which offers a much higher-level interface (and which is portable to other platforms too). This means that the interface has to be crafted directly. This can be done with either SWIG or critcl. This example shows how to do it the latter way:
Low level interface
<lang tcl>package provide xlib 1 package require critcl
critcl::clibraries -L/usr/X11/lib -lX11 critcl::ccode {
#include <X11/Xlib.h> static Display *d; static GC gc;
}
- Display connection functions
critcl::cproc XOpenDisplay {Tcl_Interp* interp char* name} ok {
d = XOpenDisplay(name[0] ? name : NULL); if (d == NULL) {
Tcl_AppendResult(interp, "cannot open display", NULL); return TCL_ERROR;
} gc = DefaultGC(d, DefaultScreen(d)); return TCL_OK;
} critcl::cproc XCloseDisplay {} void {
XCloseDisplay(d);
}
- Basic window functions
critcl::cproc XCreateSimpleWindow {
int x int y int width int height int events
} int {
int s = DefaultScreen(d); Window w = XCreateSimpleWindow(d, RootWindow(d,s), x, y, width, height, 0,
BlackPixel(d,s), WhitePixel(d,s));
XSelectInput(d, w, ExposureMask | events); return (int) w;
} critcl::cproc XDestroyWindow {int w} void {
XDestroyWindow(d, (Window) w);
} critcl::cproc XMapWindow {int w} void {
XMapWindow(d, (Window) w);
} critcl::cproc XUnmapWindow {int w} void {
XUnmapWindow(d, (Window) w);
}
- Event receiver
critcl::cproc XNextEvent {Tcl_Interp* interp} char* {
XEvent e; XNextEvent(d, &e); switch (e.type) {
case Expose: return "expose"; case KeyPress: return "key"; /* etc. This is a cheap hack version. */ default: return "?";
}
}
- Painting functions
critcl::cproc XFillRectangle {int w int x int y int width int height} void {
XFillRectangle(d, (Window)w, gc, x, y, width, height);
} critcl::cproc XDrawString {int w int x int y Tcl_Obj* msg} void {
int len; const char *str = Tcl_GetStringFromObj(msg, &len); XDrawString(d, (Window)w, gc, x, y, str, len);
}</lang> Note that this only does enough for this demo. A full adaptation is too long for RosettaCode...
This could then be used like this: <lang tcl>package require xlib
XOpenDisplay {} set w [XCreateSimpleWindow 10 10 100 100 1] XMapWindow $w while {[XNextEvent] == "expose"} {
XFillRectangle $w 20 20 10 10 XDrawString $w 10 50 "Hello, World!"
} XDestroyWindow $w XCloseDisplay</lang>
Higher level interface
Just because there is a low level package does not mean that it is pleasant to use from Tcl code. Therefore this second package wraps it up inside a higher-level package that provides a more natural way of interacting. <lang tcl>package require Tcl 8.6 package provide x11 1
namespace eval ::x {
namespace export {[a-z]*} namespace ensemble create variable mask array set mask {
KeyPress 1 KeyRelease 2 ButtonPress 4 ButtonRelease 8
}
proc display {script} {
XOpenDisplay {} catch {uplevel 1 $script} msg opts XCloseDisplay return -options $opts $msg
} proc eventloop {var handlers} {
upvar 1 $var v while 1 { set v [XNextEvent] uplevel 1 [list switch $v $handlers] }
}
oo::class create window {
variable w
constructor {x y width height events} {
set m 0 variable ::x::mask foreach e $events {catch {incr m $mask($e)}} set w [XCreateSimpleWindow $x $y $width $height $m] } method map {} { XMapWindow $w } method unmap {} { XUnmapWindow $w } method fill {x y width height} { XFillRectangle $w $x $y $width $height } method text {x y string} { XDrawString $w $x $y $string } destructor { XDestroyWindow $w }
}
}</lang> This script puts the pieces together to carry out the details of the task. <lang tcl>package require x11
- With a display connection open, create and map a window
x display {
set w [x window new 10 10 100 100 KeyPress] $w map
x eventloop e {
expose { # Paint the window $w fill 20 20 10 10 $w text 10 50 "Hello, World!" } key { # Quit the event loop break }
}
$w destroy
}</lang> Improving this by adding more sophisticated event handling and more window methods is left as an exercise.
- Programming Tasks
- GUI
- ALGOL 68
- AutoHotkey/Omit
- C
- Xlib
- Common Lisp
- Common Lisp examples needing attention
- Examples needing attention
- Field knowledge needed/X11
- Field knowledge needed/X11/Common Lisp
- CLX
- OCaml
- OCaml-Xlib
- TI-83 BASIC/Omit
- TI-89 BASIC/Omit
- Python
- Python examples needing attention
- Field knowledge needed/X11/Python
- Python-Xlib
- Tcl
- Critcl