Window creation/X11

From Rosetta Code
Jump to: navigation, search
Task
Window creation/X11
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 or XCB, that draws a box and "Hello World" in a window. Implementations of this task should avoid using a toolkit as much as possible.

Contents

[edit] ALGOL 68

Works with: ALGOL 68G version tested with release mk15-0.8b.fc9.i386

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)

[edit] C

[edit] Xlib

Library: Xlib

Compile with:

  • gcc hello-x.c -L/usr/X11R6/lib -lX11 -o hello-x
#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;
}

[edit] XCB

Library: XCB

Compile with:

  • gcc -o helloxcb -lxcb helloxcb.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
 
#include <xcb/xcb.h>
 
int main ()
{
xcb_connection_t *c;
xcb_screen_t *screen;
xcb_drawable_t win;
xcb_gcontext_t foreground;
xcb_gcontext_t background;
xcb_generic_event_t *e;
uint32_t mask = 0;
uint32_t values[2];
 
char string[] = "Hello, XCB!";
uint8_t string_len = strlen(string);
 
xcb_rectangle_t rectangles[] = {
{40, 40, 20, 20},
};
 
c = xcb_connect (NULL, NULL);
 
/* get the first screen */
screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data;
 
/* root window */
win = screen->root;
 
/* create black (foreground) graphic context */
foreground = xcb_generate_id (c);
mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
values[0] = screen->black_pixel;
values[1] = 0;
xcb_create_gc (c, foreground, win, mask, values);
 
/* create white (background) graphic context */
background = xcb_generate_id (c);
mask = XCB_GC_BACKGROUND | XCB_GC_GRAPHICS_EXPOSURES;
values[0] = screen->white_pixel;
values[1] = 0;
xcb_create_gc (c, background, win, mask, values);
 
/* create the window */
win = xcb_generate_id(c);
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
values[0] = screen->white_pixel;
values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS;
xcb_create_window (c, /* connection */
XCB_COPY_FROM_PARENT, /* depth */
win, /* window Id */
screen->root, /* parent window */
0, 0, /* x, y */
150, 150, /* width, height */
10, /* border_width */
XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */
screen->root_visual, /* visual */
mask, values); /* masks */
 
/* map the window on the screen */
xcb_map_window (c, win);
 
xcb_flush (c);
 
while ((e = xcb_wait_for_event (c))) {
switch (e->response_type & ~0x80) {
case XCB_EXPOSE:
xcb_poly_rectangle (c, win, foreground, 1, rectangles);
xcb_image_text_8 (c, string_len, win, background, 20, 20, string);
xcb_flush (c);
break;
case XCB_KEY_PRESS:
goto endloop;
}
free (e);
}
endloop:
 
return 0;
}

[edit] Common Lisp

This example needs review by someone who knows about: X11
This example was written in near-total ignorance of X11 by consulting the CLX manual to find equivalents for the parts of the C example. It was also only tested with Mac OS X X11, which is not exactly normal.

If you know X11, please review this example and, as necessary, improve it or describe what should be done for someone who knows the language.

Translation of: C
Library: CLX

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.

;;; 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))

[edit] Go

Translation of: C
package main
 
import (
"code.google.com/p/x-go-binding/xgb"
"fmt"
)
 
func main() {
c, err := xgb.Dial("")
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
 
strBytes := []byte("Hello XGB!")
rectangles := []xgb.Rectangle{{40, 40, 20, 20}}
 
// get the first screen
s := c.DefaultScreen()
 
// root window
win := s.Root
 
// create black (foreground) graphic context
fg := c.NewId()
mask := uint32(xgb.GCForeground | xgb.GCGraphicsExposures)
values := []uint32{s.BlackPixel, 0}
c.CreateGC(fg, win, mask, values)
 
// create white (background) graphic context
bg := c.NewId()
mask = uint32(xgb.GCBackground | xgb.GCGraphicsExposures)
values[0] = s.WhitePixel // (values[1] still 0)
c.CreateGC(bg, win, mask, values)
 
// create the window
win = c.NewId()
mask = xgb.CWBackPixel | xgb.CWEventMask
// values[0] still s.WhitePixel
values[1] = xgb.EventMaskExposure | xgb.EventMaskKeyPress
c.CreateWindow(0, win, s.Root, 0, 0, 150, 150, 10,
xgb.WindowClassInputOutput, s.RootVisual, mask, values)
c.MapWindow(win)
 
for {
event, err := c.WaitForEvent()
if err != nil {
fmt.Println(err)
return
}
switch event.(type) {
case xgb.ExposeEvent:
c.PolyRectangle(win, fg, rectangles)
c.ImageText8(win, bg, 20, 20, strBytes)
case xgb.KeyPressEvent:
return
}
}
}

Screen capture:

Go-x11.png

[edit] GUISS

Graphical User Interface Support Script is really a language for operating a computer, rather than programming one, so we cannot do this via X11 libraries. The example uses leafpad for our open window, and the box symbols to enclose our text:

Start,Programs,Applications,Editors,Leafpad,Textbox,
Type:[openbox]Hello World[pling][closebox]

[edit] Icon and Unicon

Icon and Unicon provide a portable graphics implementation that does not rely upon a toolkit. The intent is to be platform independent and the same code runs on multiple platforms without change and producing results with only minor variations. Icon and Unicon graphics are implemented in X-Windows as well as MS-Windows and others. There are additional 3D graphics capabilities implemented using opengl.

procedure main()
W1 := open("X-Window","g","size=250,250","bg=black","fg=red") | stop("unable to open window")
FillRectangle(W1,50,50,150,150)
WDone(W1)
end
 
link graphics

graphics.icn provides graphics

Additionally, the WOpen procedure and Window.App methods are available.

[edit] Haskell

Using
Library: X11
from HackageDB
import Graphics.X11.Xlib
import Control.Concurrent (threadDelay)
 
main = do
display <- openDisplay ""
let defScr = defaultScreen display
rw <- rootWindow display defScr
 
xwin <- createSimpleWindow display rw
0 0 400 200 1
(blackPixel display defScr)
(whitePixel display defScr)
 
setTextProperty display xwin "Rosetta Code: X11 simple window" wM_NAME
 
mapWindow display xwin
 
sync display False
threadDelay (5000000)
 
destroyWindow display xwin
closeDisplay display
 

[edit] Java

Library: Java
compile
javac WindowExample.java

run

java WindowExample
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
 
public class WindowExample {
 
public static void main(String[] args) {
Runnable runnable = new Runnable() {
public void run() {
createAndShow();
}
};
SwingUtilities.invokeLater(runnable);
}
 
static void createAndShow() {
JFrame frame = new JFrame("Hello World");
frame.setSize(640,480);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
The previous example works but doesn't write any text or draw any box; the following does both.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
 
public class WindowExample extends JApplet {
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
 
g2.setStroke(new BasicStroke(2.0f));
g2.drawString("Hello java", 20, 20);
g2.setPaint(Color.blue);
g2.draw(new Rectangle2D.Double(40, 40, 20, 20));
}
 
public static void main(String s[]) {
JFrame f = new JFrame("ShapesDemo2D");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
});
JApplet applet = new ShapesDemo2D();
f.getContentPane().add("Center", applet);
f.pack();
f.setSize(new Dimension(150, 150));
f.setVisible(true);
}
}

[edit] OCaml

Library: OCaml-Xlib

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"
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;
;;

[edit] Perl

[edit] X11::Protocol

#!/usr/bin/perl -w
use strict;
use X11::Protocol;
 
my $X = X11::Protocol->new;
 
my $window = $X->new_rsrc;
$X->CreateWindow ($window,
$X->root, # parent window
'InputOutput', # class
0, # depth, copy from parent
0, # visual, copy from parent
0,0, # X,Y (window manager will override)
300,100, # width,height
0, # border width
background_pixel => $X->black_pixel,
event_mask => $X->pack_event_mask('Exposure',
'ButtonPress'),
);
 
my $gc = $X->new_rsrc;
$X->CreateGC ($gc, $window,
foreground => $X->white_pixel);
 
$X->{'event_handler'} = sub {
my %event = @_;
my $event_name = $event{'name'};
 
if ($event_name eq 'Expose') {
$X->PolyRectangle ($window, $gc, [ 10,10, # x,y top-left corner
30,20 ]); # width,height
$X->PolyText8 ($window, $gc,
10, 55, # X,Y of text baseline
[ 0, # delta X
'Hello ... click mouse button to exit.' ]);
 
} elsif ($event_name eq 'ButtonPress') {
exit 0;
}
};
 
$X->MapWindow ($window);
for (;;) {
$X->handle_input;
}

[edit] Perl 6

Translation of: C

There is not yet a X11 library in Perl 6 but we can write the minimal C mappings for this task. Notice that the mapping for XEvent requires a manual set up which depends on the number of bits of your machine. This is due to a current bug in NativeCall on MoarVM.

use NativeCall;
 
class Display is repr('CStruct') {}
class GC is repr('CStruct') {}
class XEvent is repr('CStruct') {
has int32 $.type; # for 32 bits machine
#has int $.type; # for 64 bits machine
method init { $!type = 0 }
}
 
sub XOpenDisplay(Str $name = ':0') returns Display is native('libX11') { * }
sub XDefaultScreen(Display $) returns int is native('libX11') { * }
sub XRootWindow(Display $, int $screen_number) returns int is native('libX11') { * }
sub XBlackPixel(Display $, int $screen_number) returns int is native('libX11') { * }
sub XWhitePixel(Display $, int $screen_number) returns int is native('libX11') { * }
sub XCreateSimpleWindow(
Display $, int $parent_window, int $x, int $y,
int $width, int $height, int $border_width,
int $border, int $background
) returns int is native('libX11') { * }
sub XMapWindow(Display $, int $window) is native('libX11') { * }
sub XSelectInput(Display $, int $window, int $mask) is native('libX11') { * }
sub XFillRectangle(
Display $, int $window, GC $, int $x, int $y, int $width, int $height
) is native('libX11') { * }
sub XDrawString(
Display $, int $window, GC $, int $x, int $y, Str $, int $str_length
) is native('libX11') { * }
sub XDefaultGC(Display $, int $screen) returns GC is native('libX11') { * }
sub XNextEvent(Display $, XEvent $e) is native('libX11') { * }
sub XCloseDisplay(Display $) is native('libX11') { * }
 
my Display $display = XOpenDisplay()
or die "Can not open display";
 
my int $screen = XDefaultScreen($display);
my int $window = XCreateSimpleWindow(
$display,
XRootWindow($display, $screen),
10, 10, 100, 100, 1,
XBlackPixel($display, $screen), XWhitePixel($display, $screen)
);
XSelectInput($display, $window, 1 +< 15 +| 1);
XMapWindow($display, $window);
 
my Str $msg = 'Hello, World!';
my XEvent $e .= new; $e.init;
loop {
XNextEvent($display, $e);
if $e.type == 12 {
XFillRectangle($display, $window, XDefaultGC($display, $screen), 20, 20, 10, 10);
XDrawString($display, $window, XDefaultGC($display, $screen), 10, 50, $msg, my int $ = $msg.chars);
}
elsif $e.type == 2 {
last;
}
}
XCloseDisplay($display);

[edit] PicoLisp

The following script works in the 32-bit version, using inlined C code

#!/usr/bin/picolisp /usr/lib/picolisp/lib.l
 
(load "@lib/misc.l" "@lib/gcc.l")
 
(gcc "x11" '("-lX11") 'simpleWin)
 
#include <X11/Xlib.h>
 
any simpleWin(any ex) {
any x = cdr(ex);
int dx, dy;
Display *disp;
int scrn;
Window win;
XEvent ev;
 
x = cdr(ex), dx = (int)evCnt(ex,x);
x = cdr(x), dy = (int)evCnt(ex,x);
x = evSym(cdr(x));
if (disp = XOpenDisplay(NULL)) {
char msg[bufSize(x)];
 
bufString(x, msg);
scrn = DefaultScreen(disp);
win = XCreateSimpleWindow(disp, RootWindow(disp,scrn), 0, 0, dx, dy,
1, BlackPixel(disp,scrn), WhitePixel(disp,scrn) );
XSelectInput(disp, win, ExposureMask | KeyPressMask | ButtonPressMask);
XMapWindow(disp, win);
for (;;) {
XNextEvent(disp, &ev);
switch (ev.type) {
case Expose:
XDrawRectangle(disp, win, DefaultGC(disp, scrn), 10, 10, dx-20, dy-20);
XDrawString(disp, win, DefaultGC(disp, scrn), 30, 40, msg, strlen(msg));
break;
case KeyPress:
case ButtonPress:
XCloseDisplay(disp);
return Nil;
}
}
}
return mkStr("Can't open Display");
}
/**/
 
(simpleWin 300 200 "Hello World")
(bye)

[edit] Python

[edit] Xlib

This example needs review by someone who knows about: X11
Note (stolen from CLX example): This example was written in near-total ignorance of X11 by consulting the python-xlib's examples (included in its distribution) to find equivalents for the parts of the C example.

If you know X11, please review this example and, as necessary, improve it or describe what should be done for someone who knows the language.

Translation of: C
Library: python-xlib

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 and where there is an X server. Run:

  • python xlib_hello_world.py
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()

[edit] XCB

Library: python-xcb
import xcb
from xcb.xproto import *
import xcb.render
 
def main():
conn = xcb.connect()
conn.render = conn(xcb.render.key)
 
setup = conn.get_setup()
root = setup.roots[0].root
depth = setup.roots[0].root_depth
visual = setup.roots[0].root_visual
white = setup.roots[0].white_pixel
 
window = conn.generate_id()
conn.core.CreateWindow(depth, window, root,
0, 0, 640, 480, 0,
WindowClass.InputOutput,
visual,
CW.BackPixel | CW.EventMask,
[ white, EventMask.Exposure |
EventMask.KeyPress ])
 
conn.core.MapWindow(window)
conn.flush()
 
while True:
event = conn.wait_for_event()
 
if isinstance(event, ExposeEvent):
color = (0, 0, 65535, 65535)
rectangle = (20, 20, 40, 40)
# TODO, fixme:
# I haven't been able to find what I should put for the parameter "op"
# conn.render.FillRectangles(op, window, color, 1, rectangle)
conn.flush()
 
elif isinstance(event, KeyPressEvent):
break
 
conn.disconnect()
 
main()

[edit] Racket

Using Racket's GUI which is implemented using gtk. It's not low level, but OTOH it works on Windows and OS X too.

#lang racket/gui
 
(define frame (new frame%
[label "Example"]
[width 300]
[height 300]))
(new canvas% [parent frame]
[paint-callback
(lambda (canvas dc)
(send dc set-scale 3 3)
(send dc set-text-foreground "blue")
(send dc draw-text "Don't Panic!" 0 0))])
(send frame show #t)

[edit] Scala

Library: Scala
import scala.swing.{ MainFrame, SimpleSwingApplication }
import scala.swing.Swing.pair2Dimension
 
object WindowExample extends SimpleSwingApplication {
def top = new MainFrame {
title = "Hello!"
centerOnScreen
preferredSize = ((200, 150))
}
}

[edit] 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:

[edit] Low level interface

Library: critcl
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 "type expose";
case KeyPress: return "type key";
/* etc. This is a cheap hack version. */
default: return "type ?";
}
}
 
# 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);
}

Note that this only does enough for this demo. A full adaptation is too long for RosettaCode...

This could then be used like this:

package require xlib
 
XOpenDisplay {}
set w [XCreateSimpleWindow 10 10 100 100 1]
XMapWindow $w
while {[lindex [XNextEvent] 0] == "expose"} {
XFillRectangle $w 20 20 10 10
XDrawString $w 10 50 "Hello, World!"
}
XDestroyWindow $w
XCloseDisplay

[edit] 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.

Works with: Tcl version 8.6
or
Library: TclOO
package require TclOO
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 [dict get $v type] $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
}
}
}

This script puts the pieces together to carry out the details of the task.

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 ev {
expose {
# Paint the window
$w fill 20 20 10 10
$w text 10 50 "Hello, World!"
}
key {
# Quit the event loop
break
}
}
 
$w destroy
}

Improving this by adding more sophisticated event handling and more window methods is left as an exercise. omit from|ZX Spectrum Basic|No X11 server access.}}

Personal tools
Namespaces

Variants
Actions
Community
Explore
Misc
Toolbox