Browse Source

Graph on Allegro4, Signals, Semaphore, Pthread

Artur Efimov 5 years ago
parent
commit
88881f3082

+ 18 - 3
data/bin/compile.sh

@@ -1,10 +1,25 @@
 #!/bin/bash
 # This script is run by Free Oberon on Linux. Current directory of the
 # script will be where FreeOberon executable is located.
-cd bin
-VOCDIR=../data/bin/voc
+CURDIR=$(pwd)
+FNAME=$1
+FPATH=${FNAME%/*}
+FNAME=${FNAME##*/}
+FBASE=${FNAME%.*}
+EXTPATH=$FPATH/$FBASE
+EXTNAME=$EXTPATH/$FBASE.c
+export VOCDIR=$CURDIR/data/bin/voc
 VOC=$VOCDIR/bin/voc
-$VOC -OC -cfF $2 ../Programs/$1
+CC=gcc
+if [ -f "$EXTNAME" ]; then
+  cd $EXTPATH
+  $CC -c $FBASE.c -o $FBASE.o
+  cd $CURDIR
+fi
+if [ "$FNAME" != "$FPATH" ]; then
+  cd $FPATH
+fi
+$VOC -OC -cfF $2 $FNAME
 retcode=$?
 cd ..
 exit $retcode

+ 20 - 8
data/bin/link_console.sh

@@ -2,17 +2,29 @@
 # This script is run by Free Oberon on Linux. Current directory of the
 # script will be where FreeOberon executable is located. This
 # particular script is for console programs.
-cd bin
-VOCDIR=../data/bin/voc
+CURDIR=$(pwd)
+FBASE=$1
+FPATH=${FBASE%/*}
+FBASE=${FBASE##*/}
+VOCDIR=$CURDIR/data/bin/voc
 CC=gcc
-THENAME="${1%.*}"
-ONAME="${THENAME##*/}"
-SDL2Opts=`sdl2-config --cflags --libs`
+if [ "$FNAME" != "$FPATH" ]; then
+  cd $FPATH
+fi
 shift
+
+ARGS=
+while [ "$1" != "" ]; do
+  ARGS="$ARGS $1.o"
+  if [ -f "$1/$1.c" ]; then
+    ARGS="$ARGS $1/$1.o"
+  fi
+  shift
+done
+
 $CC -fPIC -g -I $VOCDIR/C/include \
-  -o $ONAME $ONAME.o \
-  $@ \
+  -o $FBASE $FBASE.o \
+  $ARGS \
   $VOCDIR/lib/libvoc-OC.a
 retcode=$?
-cd ..
 exit $retcode

+ 27 - 10
data/bin/link_graph.sh

@@ -2,19 +2,36 @@
 # This script is run by Free Oberon on Linux. Current directory of the
 # script will be where FreeOberon executable is located. This
 # particular script is for graphical programs.
-cd bin
-VOCDIR=../data/bin/voc
+CURDIR=$(pwd)
+FBASE=$1
+FPATH=${FBASE%/*}
+FBASE=${FBASE##*/}
+VOCDIR=$CURDIR/data/bin/voc
+Al4Opts=`allegro-config --cflags --libs`
 CC=gcc
-THENAME="${1%.*}"
-ONAME="${THENAME##*/}"
-SDL2Opts=`sdl2-config --cflags --libs`
+if [ "$FNAME" != "$FPATH" ]; then
+  cd $FPATH
+fi
 shift
+
+ARGS=
+while [ "$1" != "" ]; do
+  ARGS="$ARGS $1.o"
+  if [ -f "$1/$1.c" ]; then
+    ARGS="$ARGS $1/$1.o"
+  fi
+  shift
+done
+
 $CC -fPIC -g -I $VOCDIR/C/include \
-  -o $ONAME $ONAME.o \
-  $@ \
-  $VOCDIR/lib/Graph.o $VOCDIR/lib/SDL2.o \
+  -o $FBASE $FBASE.o \
+  $ARGS \
+  $VOCDIR/lib/Graph.o $VOCDIR/lib/Allegro.o \
+  $VOCDIR/lib/Semaphore.o \
+  $VOCDIR/lib/Semaphore/Semaphore.o \
+  $VOCDIR/lib/Signals.o \
   $VOCDIR/lib/libvoc-OC.a \
-  $SDL2Opts -lSDL2_image
+  $Al4Opts -lloadpng
+
 retcode=$?
-cd ..
 exit $retcode

+ 9 - 3
data/bin/voc/src/src/compiler/OPM.Mod

@@ -795,10 +795,16 @@ MODULE OPM;  (* RC 6.3.89 / 28.6.89, J.Templ 10.7.89 / 22.7.96  *)
   END IsProbablyInstallDir;
 
   PROCEDURE FindInstallDir;
+  VAR vocdir: ARRAY 4096 OF CHAR;
   BEGIN
-    COPY(Modules.BinaryDir, InstallDir);
-    IF ~IsProbablyInstallDir(InstallDir) THEN COPY('../data/bin/voc', InstallDir) END
-    ;COPY('../data/bin/voc', InstallDir)(*!FIXME remove*)
+    Platform.GetEnv("VOCDIR", vocdir);
+    IF vocdir[0] = 0X THEN
+      (* COPY(Modules.BinaryDir, InstallDir);
+      IF ~IsProbablyInstallDir(InstallDir) THEN COPY("../data/bin/voc", InstallDir) END;*)
+      COPY("../data/bin/voc", InstallDir)
+    ELSE
+      COPY(vocdir, InstallDir)
+    END
   END FindInstallDir;
 
 BEGIN

BIN
data/images/font.bmp


BIN
data/images/font.png


+ 2 - 0
src/ALGRAPH.md

@@ -0,0 +1,2 @@
+To use Allegro4-based module Graph run (on Debian-based systems):
+sudo apt-get install libxpm-dev libxxf86dga-dev

+ 366 - 0
src/Allegro.Mod

@@ -0,0 +1,366 @@
+MODULE Allegro;
+(* Copyright 2019-2020 Arthur Yefimov
+
+This file is part of Free Oberon.
+
+Free Oberon is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Free Oberon is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+*)
+IMPORT SYSTEM;
+CONST
+  systemAutodetect* = 0;
+  systemNone*       = 1; (*!FIXME*)
+
+  gfxText*                 = -1;
+  gfxAutodetect*           = 0;
+  gfxAutodetectFullscreen* = 1;
+  gfxAutodetectWindowed*   = 2;
+  gfxSafe*                 = 3;(*!FIXME*)
+  gfxNone*                 = 4;(*!FIXME*)
+
+  (* Mouse Callback Flags *)
+  mouseFlagMove*       = 0;
+  mouseFlagLeftDown*   = 1;
+  mouseFlagLeftUp*     = 2;
+  mouseFlagRightDown*  = 3;
+  mouseFlagRightUp*    = 4;
+  mouseFlagMiddleDown* = 5;
+  mouseFlagMiddleUp*   = 6;
+  mouseFlagMoveZ*      = 7;
+
+  (* Color Conversion *)
+  colorconvKeepTrans* = 4000000H;
+
+  (* Draw Sprite Modes *)
+  drawSpriteNormal* = 0;
+  drawSpriteLit*    = 1;
+  drawSpriteTrans*  = 2;
+
+  (* Draw Sprite Flips *)
+  drawSpriteNoFlip* = 0;
+  drawSpriteHFlip*  = 1;
+  drawSpriteVFlip*  = 2;
+  drawSpriteVHFlip* = 3;
+
+TYPE
+  INT8* = SYSTEM.INT8;
+  INT16* = SYSTEM.INT16;
+
+  Bitmap* = RECORD
+    w*, h*, clip*, cl*, cr*, ct*, cb*: INTEGER;
+    vtable*, writeBank*, readBank*, dat*: SYSTEM.ADDRESS;
+    id*: INTEGER;
+    extra*: SYSTEM.ADDRESS;
+    xOfs*, yOfs*, seg*: INTEGER;
+    line*: ARRAY 4000 OF SYSTEM.ADDRESS (*!FIXME*)
+  END;
+
+  PBitmap* = POINTER TO Bitmap;
+
+  GfxDriver* = RECORD
+    id*: INTEGER;
+    name*, desc*, asciiName*: SYSTEM.ADDRESS;
+    methods*: ARRAY 23 OF SYSTEM.ADDRESS;
+    w*, h*, linear*: INTEGER;
+    bankSize*, bankGran*, vidMem*, vidPhysBase*, windowed*: INTEGER
+  END;
+
+  PGfxDriver* = POINTER TO GfxDriver;
+
+  Rgb* = RECORD r, g, b: CHAR END;
+  PRgb* = POINTER TO Rgb;
+
+  CloseButtonCb* = PROCEDURE;
+  UKeyCb* = PROCEDURE(key: INTEGER; VAR scancode: INTEGER): INTEGER;
+  ULowKeyCb* = PROCEDURE(scancode: INTEGER);
+  MouseCb* = PROCEDURE(flags: SET(*int really*));
+  TimerCb* = PROCEDURE;
+
+PROCEDURE -AAIncludeAllegroh0* '#include "Allegro.h0"';
+
+(* VAR *)
+PROCEDURE -screen*(): PBitmap
+  "((void *)screen)";
+
+(* VAR *)
+PROCEDURE -gfxDriver*(): PGfxDriver
+  "((void *)gfx_driver)";
+
+PROCEDURE -InstallAllegro*
+  (systemId: INTEGER;
+  errnoPtr: SYSTEM.ADDRESS;
+  atexitPtr: SYSTEM.ADDRESS): INTEGER
+  "install_allegro(systemId, &errno, &atexit)";
+
+PROCEDURE -Exit*
+  "allegro_exit()";
+
+PROCEDURE -SetCloseButtonCallback*(cb: CloseButtonCb)
+  "set_close_button_callback(cb)";
+
+PROCEDURE -SetColorDepth*(depth: INTEGER)
+  "set_color_depth(depth)";
+
+PROCEDURE -SetGfxMode*
+  (card, w, h, vw, vh: INTEGER): INTEGER
+  "set_gfx_mode(card, w, h, vw, vh)";
+
+PROCEDURE -InstallKeyboard*(): INTEGER
+  "install_keyboard()";
+
+PROCEDURE -InstallMouse*(): INTEGER
+  "install_mouse()";
+
+PROCEDURE -InstallTimer*(): INTEGER
+  "install_timer()";
+
+PROCEDURE -SetWindowTitle*(name: ARRAY OF CHAR)
+  "set_window_title(name)";
+
+PROCEDURE -GetDesktopResolution*(VAR w, h: INTEGER): INTEGER
+  "get_desktop_resolution(w, h)";
+
+PROCEDURE -Rest*(time: INTEGER)
+  "rest(time)";
+
+PROCEDURE -USleep*(time: INTEGER) (*Can be removed if Rest is used*)
+  "usleep(time)";
+
+PROCEDURE -VSync*
+  "vsync()";
+
+PROCEDURE -GetKeyShifts*(VAR ks: SET)
+  "*ks = key_shifts";
+
+PROCEDURE -KeyPressed*(): INTEGER
+  "keypressed()";
+
+PROCEDURE -ReadKey*(): INTEGER
+  "readkey()";
+
+PROCEDURE -UReadKey*(VAR scancode: INTEGER): INTEGER
+  "ureadkey(scancode)";
+
+PROCEDURE -SetKeyboardUCallback*(cb: UKeyCb)
+  "keyboard_ucallback = cb";
+
+PROCEDURE -SetKeyboardLowlevelCallback*(cb: ULowKeyCb)
+  "keyboard_lowlevel_callback = cb";
+
+PROCEDURE -MakeCol32*(r, g, b: INTEGER): INTEGER
+  "makecol32(r, g, b)";
+
+PROCEDURE -MakeACol32*(r, g, b, a: INTEGER): INTEGER
+  "makeacol32(r, g, b, a)";
+
+PROCEDURE -GetPixel32*(bmp: PBitmap; x, y: INTEGER): INTEGER
+  "_getpixel32((BITMAP *)bmp, x, y)";
+
+PROCEDURE -PutPixel32*(bmp: PBitmap; x, y, color: INTEGER)
+  "_putpixel32((BITMAP *)bmp, x, y, color)";
+
+PROCEDURE -Line*(bmp: PBitmap; x1, y1, x2, y2, color: INTEGER)
+  "line((BITMAP *)bmp, x1, y1, x2, y2, color)";
+
+PROCEDURE -FastLine*(bmp: PBitmap; x1, y1, x2, y2, color: INTEGER)
+  "fastline((BITMAP *)bmp, x1, y1, x2, y2, color)";
+
+PROCEDURE -HLine*(bmp: PBitmap; x1, y, x2, color: INTEGER)
+  "hline((BITMAP *)bmp, x1, y, x2, color)";
+
+PROCEDURE -VLine*(bmp: PBitmap; x, y1, y2, color: INTEGER)
+  "vline((BITMAP *)bmp, x, y1, y2, color)";
+
+PROCEDURE -Rect*(bmp: PBitmap; x1, y1, x2, y2, color: INTEGER)
+  "rect((BITMAP *)bmp, x1, y1, x2, y2, color)";
+
+PROCEDURE -RectFill*(bmp: PBitmap; x1, y1, x2, y2, color: INTEGER)
+  "rectfill((BITMAP *)bmp, x1, y1, x2, y2, color)";
+
+PROCEDURE -ClearBitmap*(bmp: PBitmap)
+  "clear_bitmap((BITMAP *)bmp)";
+
+PROCEDURE -ClearToColor*(bmp: PBitmap; color: INTEGER)
+  "clear_to_color((BITMAP *)bmp, color)";
+
+PROCEDURE -Circle*(bmp: PBitmap; x, y, r, color: INTEGER)
+  "circle((BITMAP *)bmp, x, y, r, color)";
+
+PROCEDURE -CircleFill*(bmp: PBitmap; x, y, r, color: INTEGER)
+  "circlefill((BITMAP *)bmp, x, y, r, color)";
+
+PROCEDURE -Ellipse*(bmp: PBitmap; x, y, rx, ry, color: INTEGER)
+  "ellipse((BITMAP *)bmp, x, y, rx, ry, color)";
+
+PROCEDURE -EllipseFill*(bmp: PBitmap; x, y, rx, ry, color: INTEGER)
+  "ellipsefill((BITMAP *)bmp, x, y, rx, ry, color)";
+
+PROCEDURE -FloodFill*(bmp: PBitmap; x, y, color: INTEGER)
+  "floodfill((BITMAP *)bmp, x, y, color)";
+
+PROCEDURE -GetR32*(color: INTEGER): INTEGER
+  "getr32(color)";
+
+PROCEDURE -GetG32*(color: INTEGER): INTEGER
+  "getg32(color)";
+
+PROCEDURE -GetB32*(color: INTEGER): INTEGER
+  "getb32(color)";
+
+PROCEDURE -GetA32*(color: INTEGER): INTEGER
+  "geta32(color)";
+
+(* Bitmaps *)
+
+PROCEDURE -CreateBitmap*(w, h: INTEGER): PBitmap
+  "(Allegro_Bitmap *)create_bitmap(w, h)";
+
+PROCEDURE -CreateBitmapEx*(depth, w, h: INTEGER): PBitmap
+  "(Allegro_Bitmap *)create_bitmap_ex(depth, w, h)";
+
+PROCEDURE -CreateSubBitmap*(parent: PBitmap; x, y, w, h: INTEGER): PBitmap
+  "(Allegro_Bitmap *)create_sub_bitmap((BITMAP *)parent, x, y, w, h)";
+
+PROCEDURE -DestroyBitmap*(bmp: PBitmap)
+  "destroy_bitmap((BITMAP *)bmp)";
+
+PROCEDURE -AcquireBitmap*(bmp: PBitmap)
+  "acquire_bitmap((BITMAP *)bmp)";
+
+PROCEDURE -ReleaseBitmap*(bmp: PBitmap)
+  "release_bitmap((BITMAP *)bmp)";
+
+PROCEDURE -LoadBitmap*(filename: ARRAY OF CHAR; pal: PRgb): PBitmap
+  "(Allegro_Bitmap *)load_bitmap(filename, pal)";
+
+PROCEDURE -LoadPngInit*
+  "loadpng_init()";
+
+(* Mouse *)
+
+PROCEDURE -MouseOnScreen*(): INTEGER
+  "mouse_on_screen()";
+
+PROCEDURE -SetMouseCallback*(cb: MouseCb)
+  "mouse_callback = (void *)cb";
+
+PROCEDURE -GetMouseX*(VAR x: INTEGER)
+  "*x = mouse_x";
+
+PROCEDURE -GetMouseY*(VAR y: INTEGER)
+  "*y = mouse_y";
+
+PROCEDURE -GetMouseZ*(VAR z: INTEGER)
+  "*z = mouse_z";
+
+PROCEDURE -GetMouseW*(VAR w: INTEGER)
+  "*w = mouse_w";
+
+PROCEDURE -GetMouseB*(VAR b: SET(*int*))
+  "*b = mouse_b";
+
+PROCEDURE -GetMousePos*(VAR pos: INTEGER)
+  "*pos = mouse_pos";
+
+(* Blitting *)
+
+PROCEDURE -Blit*(src, dest: PBitmap; sx, sy, dx, dy, w, h: INTEGER)
+  "blit((BITMAP *)src, (BITMAP *)dest, sx, sy, dx, dy, w, h)";
+
+PROCEDURE -StretchBlit*
+  (src, dest: PBitmap; sx, sy, sw, sh, dx, dy, dw, dh: INTEGER)
+  "stretch_blit((BITMAP *)src, (BITMAP *)dest, sx, sy, sw, sh, dx, dy, dw, dh)";
+
+PROCEDURE -MaskedBlit*
+  (src, dest: PBitmap; sx, sy, dx, dy, w, h: INTEGER)
+  "masked_blit((BITMAP *)src, (BITMAP *)dest, sx, sy, dx, dy, w, h)";
+
+PROCEDURE -MaskedStretchBlit*
+  (src, dest: PBitmap; sx, sy, sw, sh, dx, dy, dw, dh: INTEGER)
+  "masked_stretch_blit((BITMAP *)src, (BITMAP *)dest, sx, sy, sw, sh, dx, dy, dw, dh)";
+
+PROCEDURE -DrawSprite*
+  (bmp, sprite: PBitmap; x, y: INTEGER)
+  "draw_sprite((BITMAP *)bmp, (BITMAP *)sprite, x, y)";
+
+PROCEDURE -DrawSpriteEx*
+  (bmp, sprite: PBitmap; x, y, mode, flip: INTEGER)
+  "draw_sprite_ex((BITMAP *)bmp, (BITMAP *)sprite, x, y, mode, flip)";
+
+PROCEDURE -DrawSpriteVFlip*
+  (bmp, sprite: PBitmap; x, y: INTEGER)
+  "draw_sprite_v_flip((BITMAP *)bmp, (BITMAP *)sprite, x, y)";
+
+PROCEDURE -DrawSpriteHFlip*
+  (bmp, sprite: PBitmap; x, y: INTEGER)
+  "draw_sprite_h_flip((BITMAP *)bmp, (BITMAP *)sprite, x, y)";
+
+PROCEDURE -DrawSpriteVHFlip*
+  (bmp, sprite: PBitmap; x, y: INTEGER)
+  "draw_sprite_vh_flip((BITMAP *)bmp, (BITMAP *)sprite, x, y)";
+
+PROCEDURE -DrawTransSprite*
+  (bmp, sprite: PBitmap; x, y: INTEGER)
+  "draw_trans_sprite((BITMAP *)bmp, (BITMAP *)sprite, x, y)";
+
+PROCEDURE -DrawCharacterEx*
+  (bmp, sprite: PBitmap; x, y, color, bg: INTEGER)
+  "draw_character_ex((BITMAP *)bmp, (BITMAP *)sprite, x, y, color, bg)";
+
+PROCEDURE -DrawLitSprite*
+  (bmp, sprite: PBitmap; x, y, color: INTEGER)
+  "draw_lit_sprite((BITMAP *)bmp, (BITMAP *)sprite, x, y, color)";
+
+PROCEDURE -DrawGouraudSprite*
+  (bmp, sprite: PBitmap; x, y, c1, c2, c3, c4: INTEGER)
+  "draw_gouraud_sprite((BITMAP *)bmp, (BITMAP *)sprite, x, y, c1, c2, c3, c4)";
+
+PROCEDURE -SetColorConversion*(mode: INTEGER)
+  "set_color_conversion(mode)";
+
+PROCEDURE -SetAlphaBlender*
+  "set_alpha_blender()";
+
+PROCEDURE -SetColorBlender*(r, g, b, a: INTEGER)
+  "set_color_blender(r, g, b, a)";
+
+PROCEDURE -SetHueBlender*(r, g, b, a: INTEGER)
+  "set_hue_blender(r, g, b, a)";
+
+PROCEDURE -SetLuminanceBlender*(r, g, b, a: INTEGER)
+  "set_luminance_blender(r, g, b, a)";
+
+PROCEDURE -SetTransBlender*(r, g, b, a: INTEGER)
+  "set_trans_blender(r, g, b, a)";
+
+(* Timer *)
+
+PROCEDURE -InstallInt*
+  (cb: TimerCb; speed: INTEGER)
+  "install_int((void *)cb, speed)";
+
+(* Atomic *)
+
+PROCEDURE -AtomicZero*()
+  "_al_atomic_int = 0";
+
+PROCEDURE -AtomicInc*()
+  "_al_atomic_int++";
+
+PROCEDURE -AtomicDec*()
+  "_al_atomic_int--";
+
+PROCEDURE -GetAtomic*(): INTEGER
+  "_al_atomic_int";
+
+END Allegro.

+ 12 - 0
src/Allegro.h0

@@ -0,0 +1,12 @@
+#ifndef Allegro__h0
+#define Allegro__h0
+
+/*This can be removed if Al.Rest and not Al.USleep is used in Graph.Delay*/
+#include <unistd.h>
+
+#include <allegro.h>
+#include "loadpng.h"
+
+static _Atomic int _al_atomic_int;
+
+#endif

+ 6 - 5
src/Editor.Mod

@@ -1,5 +1,5 @@
 MODULE Editor;
-(* Copyright 2017-2019 Arthur Yefimov
+(* Copyright 2017-2020 Arthur Yefimov
 
 This file is part of Free Oberon.
 
@@ -16,7 +16,7 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 *)
-IMPORT OV, T := Terminal, G := Graph, Text := EditorText, Strings, Out;
+IMPORT OV, T := Terminal, G := Graph, Text := EditorText, Strings;
 CONST
   (* Direction of Selection *)
   dirLeft  = 0;
@@ -789,10 +789,11 @@ BEGIN
   OV.WindowMouseMove(c, x, y, buttons)
 END EditorMouseMove;
 
-PROCEDURE EditorTextInput(c: OV.Control; s: ARRAY OF CHAR; sym: INTEGER);
+PROCEDURE EditorTextInput(c: OV.Control; key: G.Key);
 BEGIN
-  IF sym # 0 THEN c(Editor).text.InsertChar(CHR(sym)); T.ResetCursorBlink;
-    c(Editor).text.selected := FALSE; PrintText(c(Editor))
+  IF key.sym # 0 THEN c(Editor).text.InsertChar(CHR(key.sym));
+    T.ResetCursorBlink; c(Editor).text.selected := FALSE;
+    PrintText(c(Editor))
   END
 END EditorTextInput;
 

+ 2 - 2
src/EditorText.Mod

@@ -1,5 +1,5 @@
 MODULE EditorText;
-(* Copyright 2017-2019 Arthur Yefimov
+(* Copyright 2017-2020 Arthur Yefimov
 
 This file is part of Free Oberon.
 
@@ -16,7 +16,7 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 *)
-IMPORT Config, Files, Strings, Out;
+IMPORT Config, Files, Strings;
 
 CONST
   lineLen = 256;

+ 119 - 71
src/FreeOberon.Mod

@@ -1,5 +1,5 @@
 MODULE FreeOberon;
-(* Copyright 2017-2019 Arthur Yefimov
+(* Copyright 2017-2020 Arthur Yefimov
 
 This file is part of Free Oberon.
 
@@ -17,9 +17,9 @@ You should have received a copy of the GNU General Public License
 along with Free Oberon.  If not, see <http://www.gnu.org/licenses/>.
 *)
 IMPORT G := Graph, T := Terminal, Files, Modules,
-       OV, Editor, Term, Config, Strings, Out;
+       OV, Editor, Term, Config, Strings, Out, Signals;
 CONST
-  version* = '1.0.3';
+  version* = '1.0.4'; (*Allegro4 branch*)
 
   (* Direction of Selection *)
   dirLeft  = 0;
@@ -31,14 +31,6 @@ CONST
   stateEditor   = 0;
   stateTerminal = 1;
 
-  (* Character Classes *)
-  charOther       = 0; (*!FIXME Remove these constants *)
-  charAlpha       = 1;
-  charDigit       = 2;
-  charMinusPlus   = 3;
-  charQuote       = 4;
-  charOpenBracket = 5;
-
   (* Token Classes *)
   tokenOther   = 0;
   tokenKeyword = 1;
@@ -61,7 +53,6 @@ VAR
   programFinished: BOOLEAN;
   tempWindowed: BOOLEAN; (* True if editor is in windowed mode while program is running *)
   needWindowed: BOOLEAN;
-  blockToggle: BOOLEAN; (* If true, ALT+ENTER will not toggle fullscreen *)
   sysModules: StrList;
 
   app: OV.App;
@@ -101,8 +92,7 @@ BEGIN
     END;
     INC(i)
   END;
-  RETURN lines
-END CountLines;
+RETURN lines END CountLines;
 
 PROCEDURE ShowErrors(s: ARRAY OF CHAR);
 VAR lines, width, x0, x, y, i: INTEGER;
@@ -250,10 +240,9 @@ BEGIN
   END
 END ParseErrors;
 
-PROCEDURE HandleMouseMotion;
-VAR x, y, newX, newY: INTEGER;
+PROCEDURE HandleMouseMotion(x, y: INTEGER);
+VAR newX, newY: INTEGER;
 BEGIN
-  G.GetMousePos(x, y);
   newX := x DIV T.charW;  newY := y DIV T.charH;
   IF (newX # T.mouseX) OR (newY # T.mouseY) THEN T.MouseXY(newX, newY) END
 END HandleMouseMotion;
@@ -353,10 +342,7 @@ BEGIN
   ELSE
     CASE key.code OF
       G.kEnter, G.kEnterPad:
-      IF key.mod * G.mAlt # {} THEN
-        IF blockToggle THEN blockToggle := FALSE
-        ELSE T.ToggleFullscreen; blockToggle := TRUE
-        END
+      IF key.mod * G.mAlt # {} THEN T.ToggleFullscreen
       ELSE T.Ln;
         WriteToProcess(inputBuf, inputBufLen);
         inputBufLen := 0; buf[0] := 0AX;
@@ -376,11 +362,11 @@ BEGIN
   END
 END HandleTerminalKeyDown;
 
-PROCEDURE HandleTerminalTextInput(s: ARRAY OF CHAR; sym: INTEGER);
+PROCEDURE HandleTerminalTextInput(key: G.Key);
 BEGIN
-  IF (sym # 0) & (inputBufLen < LEN(inputBuf)) THEN
-    inputBuf[inputBufLen] := CHR(sym); INC(inputBufLen);
-    T.Write(CHR(sym))
+  IF (key.sym # 0) & (inputBufLen < LEN(inputBuf)) THEN
+    inputBuf[inputBufLen] := CHR(key.sym); INC(inputBufLen);
+    T.Write(CHR(key.sym))
   END
 END HandleTerminalTextInput;
 
@@ -392,14 +378,14 @@ BEGIN quit := FALSE;
     G.WaitEvents(50);
     WHILE G.PollEvent(event) DO
       CASE event.type OF
-        G.mouseMove: HandleMouseMotion
+        G.mouseMove: HandleMouseMotion(event.x, event.y)
       | G.keyDown: HandleTerminalKeyDown(event.key, quit)
-      | G.textInput: HandleTerminalTextInput(event.s, event.key.sym)
+      | G.textInput: HandleTerminalTextInput(event.key)
+      | G.timer: T.Act
       ELSE
       END
     END;
     PollProgram;
-    T.Act;
     IF T.Draw() THEN G.Flip ELSE G.RepeatFlip END
   UNTIL quit
 END RunTerminal;
@@ -408,11 +394,10 @@ PROCEDURE IsSysModule(name: ARRAY OF CHAR): BOOLEAN;
 VAR p: StrList;
 BEGIN p := sysModules;
   WHILE (p # NIL) & (p.s # name) DO p := p.next END;
-  RETURN p # NIL
-END IsSysModule;
+RETURN p # NIL END IsSysModule;
 
 PROCEDURE ModuleExists(s: ARRAY OF CHAR): BOOLEAN;
-VAR fname: ARRAY 120 OF CHAR;
+VAR fname: ARRAY 250 OF CHAR;
   F: Files.File;
   exists: BOOLEAN;
 BEGIN
@@ -423,6 +408,45 @@ BEGIN
   IF F # NIL THEN Files.Close(F) END;
 RETURN exists END ModuleExists;
 
+PROCEDURE ExtModuleExists(s: ARRAY OF CHAR): BOOLEAN;
+VAR fname: ARRAY 250 OF CHAR;
+  F: Files.File;
+  exists: BOOLEAN;
+BEGIN
+  fname := 'Programs/'; Strings.Append(s, fname);
+  Strings.Append('/', fname); Strings.Append(s, fname);
+  Strings.Append('.c', fname);
+  F := Files.Old(fname);
+  exists := F # NIL;
+  IF F # NIL THEN Files.Close(F) END;
+RETURN exists END ExtModuleExists;
+
+PROCEDURE PrependPath(VAR mod: ARRAY OF CHAR; path: ARRAY OF CHAR);
+VAR i, j: INTEGER;
+BEGIN i := 0;
+  WHILE (i < LEN(path)) & (path[i] # 0X) DO INC(i) END;
+  WHILE (i >= 0) & (path[i] # '/') DO DEC(i) END;
+  INC(i);
+  IF i > 0 THEN j := 0;
+    WHILE (j < LEN(mod)) & (mod[j] # 0X) DO INC(j) END;
+    IF LEN(mod) >= i + j THEN
+      WHILE j >= 0 DO mod[j + i] := mod[j]; DEC(j) END;
+      j := 0;
+      WHILE j < i DO mod[j] := path[j]; INC(j) END
+    END
+  END
+END PrependPath;
+
+PROCEDURE RemovePath(s: ARRAY OF CHAR; VAR res: ARRAY OF CHAR);
+VAR i: INTEGER;
+BEGIN i := 0;
+  WHILE (i < LEN(s)) & (s[i] # 0X) DO INC(i) END;
+  WHILE (i >= 0) & (s[i] # '/') DO DEC(i) END;
+  IF i >= 0 THEN Strings.Extract(s, i + 1, 80, res)
+  ELSE COPY(s, res)
+  END
+END RemovePath;
+
 PROCEDURE RunCommand(filename: ARRAY OF CHAR;
     link, graph, main: BOOLEAN; list: StrList): BOOLEAN;
 CONST bufLen = 20480;
@@ -450,18 +474,20 @@ BEGIN
     COPY('data/bin/', cmd); Strings.Append(command, cmd);
     Strings.Append('.sh ', cmd)
   END;
+  Strings.Append('Programs/', cmd);(*!FIXME*)
   Strings.Append(filename, cmd);
   IF main THEN Strings.Append(' -m', cmd)
   ELSIF link & (list # NIL) THEN
     p := list;
     WHILE p.next # NIL DO
       IF ModuleExists(p.s) THEN
-        Strings.Append(' ', cmd); Strings.Append(p.s, cmd);
-        Strings.Append('.o', cmd)
+        RemovePath(p.s, s); Strings.Append(' ', cmd);
+        Strings.Append(s, cmd)
       END;
       p := p.next
     END
   END;
+  Out.String('cmd=');Out.String(cmd);Out.Ln;(*!FIXME*)
   success := (Term.RunProcess(cmd, buf, bufLen, len, err) # 0) &
              (err = 0);
   IF ~success THEN
@@ -480,15 +506,14 @@ BEGIN
     ELSE ShowErrors(buf)
     END
   END;
-  RETURN success
-END RunCommand;
+RETURN success END RunCommand;
 
 PROCEDURE Compile(filename: ARRAY OF CHAR; main: BOOLEAN): BOOLEAN;
 BEGIN RETURN RunCommand(filename, FALSE, FALSE, main, NIL)
 END Compile;
 
 PROCEDURE Link(filename: ARRAY OF CHAR;
-    graph: BOOLEAN; list: StrList): BOOLEAN;
+  graph: BOOLEAN; list: StrList): BOOLEAN;
 BEGIN RETURN RunCommand(filename, TRUE, graph, FALSE, list)
 END Link;
 
@@ -498,10 +523,12 @@ PROCEDURE ResetSysModules;
   BEGIN NEW(p); p.s := s; p.next := sysModules; sysModules := p
   END Add;
 BEGIN sysModules := NIL;
-  Add('SYSTEM');  Add('Texts');    Add('Files');  Add('Strings');
-  Add('In');      Add('Out');      Add('Math');   Add('MathL');
-  Add('Modules'); Add('Platform'); Add('Oberon'); Add('Reals');
-  Add('VT100');   Add('Graph');    Add('SDL2');   Add('Term')
+  Add('SYSTEM');  Add('Texts');     Add('Files');   Add('Strings');
+  Add('In');      Add('Out');       Add('Math');    Add('MathL');
+  Add('Modules'); Add('Platform');  Add('Oberon');  Add('Reals');
+  Add('VT100');   Add('Graph');     Add('SDL2');    Add('Allegro');
+  Add('Signals'); Add('Processes'); Add('Pthread'); Add('Semaphore');
+  Add('Term')(*!FIXME Term?*)
 END ResetSysModules;
 
 PROCEDURE CompileAll(modules: StrList): BOOLEAN;
@@ -526,29 +553,44 @@ BEGIN
         s := p.s; Strings.Append('.Mod', s);
         IF ~Compile(s, TRUE) THEN ok := FALSE END
       END;
-      IF ~Link(p.s, graph, modules) THEN ok := FALSE END
+      IF ok & ~Link(p.s, graph, modules) THEN ok := FALSE END
     END
   ELSE ok := FALSE
   END;
 RETURN ok END CompileAll;
 
 PROCEDURE RunProgram(prg: ARRAY OF CHAR);
-VAR cmd: ARRAY 128 OF CHAR;
+VAR cmd, name, dir: ARRAY 256 OF CHAR;
     x: INTEGER;
 BEGIN
-  (* Extract 'Prg' from 'Prg.Mod' or 'dir/Prg.Mod' *)
-  x := Strings.Length(prg);
-  WHILE (x > 0) & (prg[x] # '.') DO DEC(x) END;
-  IF prg[x] = '.' THEN prg[x] := 0X END;
-  WHILE (x >= 0) & (prg[x] # '/') DO DEC(x) END;
-  IF x >= 0 THEN Strings.Delete(prg, 0, x + 1) END;
-
-  (* Construct 'bin/MyProg' or 'bin\MyProg' *)
-  IF Config.isWindows THEN COPY('bin\', cmd) ELSE COPY('bin/', cmd) END;
-  Strings.Append(prg, cmd);
-
-  IF ~Term.StartProcess(cmd) THEN
-    T.PutString(0, T.charsY - 1, ' Program execution failed ', 15, 4, 0);
+  (* Leave only "dir/d/Prg" from "dir/d/Prg.Mod" *)
+  (* (or "Prg" from "Prg.Mod") *)
+  x := Strings.Length(prg) - 1;
+  WHILE (x > 0) & (prg[x] # ".") DO DEC(x) END;
+  IF x > 0 THEN prg[x] := 0X END;
+
+  (* Split "dir/d/Prg" to "dir/d" and "Prg" *)
+  (* (or "Prg" to "" and "Prg") *)
+  WHILE (x > 0) & (prg[x] # "/") DO DEC(x) END;
+  IF x > 0 THEN
+    Strings.Extract(prg, x + 1, 80, name);
+    prg[x] := 0X
+  ELSE COPY(prg, name); cmd[0] := 0X
+  END;
+
+  IF Config.isWindows THEN x := 0;
+    WHILE prg[x] # 0X DO
+      IF prg[x] = "/" THEN prg[x] := "\" END;
+      INC(x)
+    END
+  END;
+
+  dir := "Programs/";
+  IF Config.isWindows THEN dir[Strings.Length(dir) - 1] := "\" END;
+  Strings.Append(cmd, dir);
+
+  IF ~Term.StartProcessDir(name, dir) THEN
+    T.PutString(0, T.charsY - 1, " Program execution failed ", 15, 4, 0);
     IF T.Draw() THEN G.Flip; G.Pause END
   ELSE
     programFinished := FALSE;
@@ -678,8 +720,13 @@ BEGIN NEW(top); top.next := NIL; p := top;
           mod := s;
           GetSym(R, ch, s);
           IF s = ':=' THEN GetSym(R, ch, s); mod := s; GetSym(R, ch, s) END;
-          IF IsSysModule(mod) OR ModuleExists(mod) THEN
+          IF IsSysModule(mod) THEN
             NEW(p.next); p := p.next; p.next := NIL; p.s := mod
+          ELSE
+            PrependPath(mod, modname);
+            IF ModuleExists(mod) THEN
+              NEW(p.next); p := p.next; p.next := NIL; p.s := mod
+            END
           END;
           IF s = ',' THEN GetSym(R, ch, s) ELSE ok := FALSE END
         END
@@ -742,7 +789,7 @@ BEGIN i := 0;
   modname[i] := 0X
 END GetModuleName;
 
-PROCEDURE OnBuild(c: OV.Control);
+PROCEDURE OnCompileAndRun(c: OV.Control);
 VAR w: OV.Window; graph: BOOLEAN;
   primaryFile, modname: ARRAY 256 OF CHAR;
   modules: StrList;
@@ -753,7 +800,7 @@ BEGIN w := c.app.windows;
       COPY(w(Editor.Editor).filename, primaryFile);
       GetModuleName(primaryFile, modname);
       modules := UsedModuleList(modname);
-      (*DebugStrList(modules);*)
+      (*DebugStrList(modules);(*!FIXME*)*)
       graph := ImportsGraph(modules);
       needWindowed := graph;
       IF CompileAll(modules) THEN
@@ -763,7 +810,7 @@ BEGIN w := c.app.windows;
       END
     END
   END
-END OnBuild;
+END OnCompileAndRun;
 
 PROCEDURE HelpAbout(c: OV.Control);
 BEGIN
@@ -782,7 +829,8 @@ BEGIN E := app.windows; count := 0;
       INC(count);
       IF W = E THEN W := NIL ELSE W := W.next END
     END;
-    IF count < 4 THEN cols := 1
+    IF count = 2 THEN cols := 2
+    ELSIF count < 4 THEN cols := 1
     ELSIF count < 9 THEN cols := 2
     ELSE cols := 3
     END;
@@ -878,17 +926,17 @@ BEGIN
   (*!TODO*) m.children.prev.status := OV.disabled;
   OV.AddMenu(app, m);
   m := OV.NewMenu('&Run', '', 0, NIL);
-  OV.Add(m, OV.NewMenu('&Run', 'Ctrl+F9', OV.hCtrlF9, OnBuild));
+  OV.Add(m, OV.NewMenu('&Run', 'Ctrl+F9', OV.hCtrlF9, OnCompileAndRun));
   OV.Add(m, OV.NewMenu('Run &Directory...', '', 0, NIL));
   (*!TODO*) m.children.prev.status := OV.disabled;
   OV.Add(m, OV.NewMenu('P&arameters...', '', 0, NIL));
   (*!TODO*) m.children.prev.status := OV.disabled;
   OV.AddMenu(app, m);
   m := OV.NewMenu('&Compile', '', 0, NIL);
-  OV.Add(m, OV.NewMenu('&Compile', 'Alt+F9', OV.hAltF9, OnBuild));
-  OV.Add(m, OV.NewMenu('&Make', 'Shift+F9', OV.hShiftF9, OnBuild));
-  OV.Add(m, OV.NewMenu('Make && &Run', 'F9', OV.hF9, OnBuild));
-  OV.Add(m, OV.NewMenu('&Build', '', 0, OnBuild));
+  OV.Add(m, OV.NewMenu('&Compile', 'Alt+F9', OV.hAltF9, OnCompileAndRun));
+  OV.Add(m, OV.NewMenu('&Make', 'Shift+F9', OV.hShiftF9, OnCompileAndRun));
+  OV.Add(m, OV.NewMenu('Make && &Run', 'F9', OV.hF9, OnCompileAndRun));
+  OV.Add(m, OV.NewMenu('&Build', '', 0, OnCompileAndRun));
   OV.AddMenu(app, m);
   m := OV.NewMenu('&Debug', '', 0, NIL);
   OV.Add(m, OV.NewMenu('&Output', '', 0, NIL));
@@ -947,6 +995,7 @@ BEGIN
   OV.Add(m, OV.NewMenu('Cl&ose all', '', 0, OV.CloseAllWindows));
   OV.Add(m, OV.NewMenu('-', '', 0, NIL));
   OV.Add(m, OV.NewMenu('&Size/Move', 'Ctrl+F5', OV.hCtrlF5, NIL));
+  (*!TODO*) m.children.prev.status := OV.disabled;
   OV.Add(m, OV.NewMenu('&Zoom', 'F5', OV.hF5, OV.ZoomCurWindow));
   OV.Add(m, OV.NewMenu('&Next', 'F6', OV.hF6, OV.NextWindow));
   OV.Add(m, OV.NewMenu('&Previous', 'Shift+F6', OV.hShiftF6, OV.PrevWindow));
@@ -976,8 +1025,9 @@ BEGIN
   OV.AddStatusbar(app, OV.NewQuickBtn('Help', 'F1', 0, HelpAbout));
   OV.AddStatusbar(app, OV.NewQuickBtn('Save', 'F2', 0, FileSave));
   OV.AddStatusbar(app, OV.NewQuickBtn('Open', 'F3', 0, FileOpen));
-  OV.AddStatusbar(app, OV.NewQuickBtn('Compile & Run', 'F9', 0, OnBuild));
-  OV.AddStatusbar(app, OV.NewQuickBtn('Local menu', 'Alt+F10', 0, NIL))
+  OV.AddStatusbar(app, OV.NewQuickBtn('Compile & Run', 'F9', 0, OnCompileAndRun));
+  OV.AddStatusbar(app, OV.NewQuickBtn('Local menu', 'Alt+F10', 0, NIL));
+  (*!TODO*) app.statusbar.children.prev.status := OV.disabled
 END InitIDE;
 
 PROCEDURE OpenFiles(VAR fnames: Fnames);
@@ -993,7 +1043,7 @@ PROCEDURE ParseArgs(VAR fs, sw: BOOLEAN;
     VAR fnames: Fnames);
 VAR i, nofnames: INTEGER;
   s: ARRAY 256 OF CHAR;
-BEGIN fs := TRUE; sw := FALSE; i := 0; nofnames := 0;
+BEGIN fs := FALSE; sw := FALSE; i := 0; nofnames := 0;
   WHILE i # Modules.ArgCount DO
     Modules.GetArg(i, s);
     IF s = '--window' THEN fs := FALSE
@@ -1015,14 +1065,12 @@ BEGIN
   IF T.Init(fs, sw) THEN
     InitIDE;
     needWindowed := TRUE;
-    blockToggle := FALSE;
     ResetSysModules;
     OpenFiles(fnames);
     success := TRUE
   ELSE Out.String('Terminal init failed.'); Out.Ln
   END;
-  RETURN success
-END Init;
+RETURN success END Init;
 
 BEGIN
   IF ~Init() THEN Out.String('Could not initialize.'); Out.Ln

File diff suppressed because it is too large
+ 553 - 470
src/Graph.Mod


+ 39 - 21
src/Makefile_linux

@@ -2,8 +2,7 @@ PROG=FreeOberon
 OS=linux
 VOC=../data/bin/voc/bin/voc
 CC=gcc
-SDL2Opts=`sdl2-config --cflags --libs`
-VER=1.0.3
+VER=1.0.4
 
 all: prepare voc $(PROG) install
 
@@ -11,15 +10,17 @@ fo: $(PROG)
 
 $(PROG): $(PROG).sym
 	$(CC) -fPIC -g -I ../data/bin/voc/C/include \
-		-o $(PROG) $(PROG).o Graph.o SDL2.o \
+		-o $(PROG) $(PROG).o Graph.o Allegro.o \
 		OV.o Editor.o Term.o Terminal.o \
-		EditorText.o Config.o term/term.o \
-		../data/bin/voc/lib/libvoc-OC.a \
-		$(SDL2Opts) -lSDL2_image && \
+		EditorText.o Signals.o Pthread.o Semaphore.o \
+    Config.o Term/Term.o Semaphore/Semaphore.o \
+    al_icon/al_icon.o \
+		../data/bin/voc/lib/libvoc-OC.a -lpthread \
+    `allegro-config --libs` -lloadpng && \
 		mv $(PROG) ..
 
 $(PROG).sym: $(PROG).Mod EditorText.sym Terminal.sym OV.sym \
-		Editor.sym Term.sym Graph.sym SDL2.sym
+		Editor.sym Term.sym Graph.sym al_icon/al_icon.o
 	$(VOC) -OC -cesF -m $(PROG).Mod
 
 OV.sym: OV.Mod Terminal.sym Graph.sym
@@ -28,7 +29,7 @@ OV.sym: OV.Mod Terminal.sym Graph.sym
 Editor.sym: Editor.Mod Terminal.sym EditorText.sym OV.sym Graph.sym
 	$(VOC) -OC -cesF Editor.Mod
 
-Term.sym: Term.Mod term/term.o
+Term.sym: Term.Mod Term/Term.o
 	$(VOC) -OC -cesF Term.Mod
 
 Terminal.sym: Terminal.Mod Graph.sym
@@ -40,20 +41,35 @@ EditorText.sym: EditorText.Mod Config.sym
 Config.sym: Config_$(OS).Mod
 	$(VOC) -OC -cesF Config_$(OS).Mod
 
-term/term.o: term/term_$(OS).c
-	$(CC) -c term/term_$(OS).c -o term/term.o
+Term/Term.o: Term/Term_$(OS).c
+	$(CC) -c Term/Term_$(OS).c -o Term/Term.o
 
-Graph.sym: Graph.Mod SDL2.sym
+Graph.sym: Graph.Mod Allegro.sym Signals.sym
 	$(VOC) -OC -cesF Graph.Mod
 
-SDL2.sym: SDL2.Mod
-	$(VOC) -OC -cesF SDL2.Mod
+Allegro.sym: Allegro.Mod
+	$(VOC) -OC -cesF Allegro.Mod
 
-.PHONY: clean cleanfo cleanall install pack prepare
+Signals.sym: Signals.Mod Semaphore.sym Pthread.sym
+	$(VOC) -OC -cesF Signals.Mod
+
+Pthread.sym: Pthread.Mod
+	$(VOC) -OC -cesF Pthread.Mod
+
+Semaphore.sym: Semaphore.Mod Semaphore/Semaphore.o
+	$(VOC) -OC -cesF Semaphore.Mod
+
+Semaphore/Semaphore.o: Semaphore/Semaphore.c
+	$(CC) -c Semaphore/Semaphore.c -o Semaphore/Semaphore.o
+
+al_icon/al_icon.o: al_icon/al_icon.c
+	$(CC) -c al_icon/al_icon.c -o al_icon/al_icon.o
+
+.PHONY: clean cleanfo cleanall install pack prepare run
 
 cleanfo:
-	rm -f *.c *.h *.o *.sym term/term.o .tmp..* \
-		../Programs/.tmp..* ../bin/*
+	rm -f *.c *.h *.o *.sym Term/*.o Semaphore/*.o al_icon/*.o .tmp..* \
+		../Programs/.tmp..*
 
 clean: cleanfo
 	make -C ../data/bin/voc/src clean
@@ -82,9 +98,11 @@ pack: clean prepare
 voc:
 	make -C ../data/bin/voc/src
 
-install:
-	cp Graph.sym SDL2.sym ../data/bin/voc/C/sym
-	cp Graph.h SDL2.h SDL2.h0 ../data/bin/voc/C/include
-	cp Graph.o SDL2.o ../data/bin/voc/lib
-	mkdir -p ../bin
+run: fo
 
+install:
+	cp Graph.sym Allegro.sym ../data/bin/voc/C/sym
+	cp Graph.h Allegro.h Allegro.h0 ../data/bin/voc/C/include
+	cp Graph.o Allegro.o Signals.o Semaphore.o Pthread.o ../data/bin/voc/lib
+	mkdir -p ../data/bin/voc/lib/Semaphore
+	cp Semaphore/Semaphore.o ../data/bin/voc/lib/Semaphore

+ 29 - 38
src/OV.Mod

@@ -1,5 +1,5 @@
 MODULE OV;
-(* Copyright 2017-2019 Arthur Yefimov
+(* Copyright 2017-2020 Arthur Yefimov
 
 This file is part of Free Oberon.
 
@@ -16,7 +16,7 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 *)
-IMPORT G := Graph, T := Terminal, Strings, Out, SYSTEM;
+IMPORT G := Graph, T := Terminal, Strings, SYSTEM, Out;
 CONST
   (* Control Statuses *)
   normal*   = 0;
@@ -585,7 +585,7 @@ TYPE
     refresh*: PROCEDURE (c: Control);
     click*: PROCEDURE (c: Control);
     keyDown*: PROCEDURE (c: Control; key: G.Key);
-    textInput*: PROCEDURE (c: Control; s: ARRAY OF CHAR; sym: INTEGER);
+    textInput*: PROCEDURE (c: Control; key: G.Key);
     getFocus*: PROCEDURE (c: Control);
     lostFocus*: PROCEDURE (c: Control);
     mouseMove*: PROCEDURE (c: Control; x, y: INTEGER; buttons: SET);
@@ -650,8 +650,6 @@ VAR
   menuMethod-: ControlMethod;
   quickBtnMethod-: ControlMethod;
 
-  blockToggle: BOOLEAN; (* If true, ALT+ENTER will not toggle fullscreen *)
-
 PROCEDURE DrawAppWindows*(app: App);
 VAR w, br: Control;
 BEGIN
@@ -1069,6 +1067,7 @@ BEGIN
       IF p.next = c THEN p := NIL ELSE p := p.next END
     END;
     IF p # NIL THEN
+      G.RemoveTextInputEvent;
       c.status := normal; p.status := selected;
       c.parent(Menu).lastSelected := p; p.do.click(p)
     END
@@ -1127,16 +1126,8 @@ BEGIN p := NIL;
       IF menu # app.menu THEN
         REPEAT p := menu.children; br := p;
           WHILE (p # NIL) & ~((x >= p.x) & (x < p.x + p.w) & (y = p.y)) DO
-            (*Out.String('x=');Out.Int(x,2); Out.String(' y=');Out.Int(x,2);
-            Out.String(' p.x=');Out.Int(p.x,2); Out.String(' p.w=');Out.Int(p.w,2);
-            Out.String(' p.y=');Out.Int(p.y,2); Out.String(' p.h=');Out.Int(p.h,2); Out.Ln;*)
             IF p.next = br THEN p := NIL ELSE p := p.next END
           END;
-          (*IF p # NIL THEN 
-            Out.String('x=');Out.Int(x,2); Out.String(' y=');Out.Int(x,2);
-            Out.String(' p.x=');Out.Int(p.x,2); Out.String(' p.w=');Out.Int(p.w,2);
-            Out.String(' p.y=');Out.Int(p.y,2); Out.String(' p.h=');Out.Int(p.h,2); Out.String(' - FOUND');Out.Ln;
-          END;*)
           IF p = NIL THEN menu := menu.parent; INC(y, menu.h) END
         UNTIL (menu = app.menu) OR (p # NIL);
         IF p # NIL THEN DEC(x, p.x); DEC(y, p.y) END
@@ -1266,15 +1257,17 @@ PROCEDURE QuickBtnDraw(c: Control; x, y: INTEGER);
 VAR i, w, bg, fg: INTEGER;
   hl: BOOLEAN; (* Highlighted letter *)
 BEGIN INC(x, c.x); INC(y, c.y);
-  IF c.status = normal THEN bg := 7 ELSE bg := 2 END;
+  IF c.status = selected THEN bg := 2 ELSE bg := 7 END;
+  IF c.status = disabled THEN fg := 8 ELSE fg := 4 END;
   T.PutChar(x, y, ' ', 0, bg); INC(x); i := 0;
   WHILE c(Menu).hint[i] # 0X DO
-    T.PutChar(x, y, c(Menu).hint[i], 4, bg);
+    T.PutChar(x, y, c(Menu).hint[i], fg, bg);
     INC(x); INC(i)
   END;
+  IF c.status = disabled THEN fg := 8 ELSE fg := 0 END;
   T.PutChar(x, y, ' ', 0, bg); INC(x); i := 0;
   WHILE c.caption[i] # 0X DO
-    T.PutChar(x, y, c.caption[i], 0, bg);
+    T.PutChar(x, y, c.caption[i], fg, bg);
     INC(x); INC(i)
   END;
   T.PutChar(x, y, ' ', 0, bg)
@@ -1490,11 +1483,11 @@ BEGIN e := c(Edit); redraw := TRUE;
   IF redraw THEN NeedRedraw(c.app); T.ResetCursorBlink END
 END EditKeyDown;
 
-PROCEDURE EditTextInput*(c: Control; s: ARRAY OF CHAR; sym: INTEGER);
+PROCEDURE EditTextInput*(c: Control; key: G.Key);
 VAR e: Edit;
 BEGIN
-  IF sym # 0 THEN e := c(Edit);
-    InsertChar(CHR(sym), e.pos, e.caption, e.len);
+  IF key.sym # 0 (*!FIXME*) THEN e := c(Edit);
+    InsertChar(CHR(key.sym), e.pos, e.caption, e.len);
     IF e.pos < e.len THEN INC(e.pos) END;
     NeedRedraw(c.app); T.ResetCursorBlink
   END
@@ -1819,11 +1812,12 @@ BEGIN app.statusText := text
 END SetStatusText;
 
 PROCEDURE CheckMenuOpenKey(app: App; key: G.Key): BOOLEAN;
-VAR p, q, br: Control; found: BOOLEAN;
+VAR p, q, br: Control; found: BOOLEAN; ch: CHAR;
 BEGIN found := FALSE;
-  IF (ORD('a') <= key.sym) & (key.sym <= ORD('z')) & ~HasModalWindow(app) THEN
+  IF (1 <= key.code) & (key.code <= 26) & ~HasModalWindow(app) THEN
+    ch := CHR(ORD('a') - 4 + key.code); (*!FIXME i.e. for Russian*)
     p := app.menu.children.prev; br := p;
-    REPEAT p := p.next; found := MenuHotkey(p(Menu), CHR(key.sym))
+    REPEAT p := p.next; found := MenuHotkey(p(Menu), ch)
     UNTIL found OR (p = br); (* !FIXME use loop pattern *)
     IF found THEN SetFocus(app, NIL); p.do.click(p) END
   END;
@@ -1874,9 +1868,7 @@ BEGIN handled := FALSE; p := app.cur;
   END;
   IF ~handled THEN
     IF (key.code = G.kEnter) & (key.mod * G.mAlt # {}) THEN
-      IF blockToggle THEN blockToggle := FALSE
-      ELSE T.ToggleFullscreen; blockToggle := TRUE
-      END
+      T.ToggleFullscreen
     ELSIF p # NIL THEN
       IF p.do.keyDown # NIL THEN p.do.keyDown(p, key) END;
       IF (p.parent # NIL) & (p.parent IS Window) &
@@ -1887,10 +1879,10 @@ BEGIN handled := FALSE; p := app.cur;
   END
 END OnKeyDown;
 
-PROCEDURE OnTextInput(app: App; s: ARRAY OF CHAR; sym: INTEGER);
+PROCEDURE OnTextInput(app: App; key: G.Key);
 BEGIN
   IF (app.cur # NIL) & (app.cur.do.textInput # NIL) THEN
-    app.cur.do.textInput(app.cur, s, sym)
+    app.cur.do.textInput(app.cur, key)
   END
 END OnTextInput;
 
@@ -1966,32 +1958,31 @@ BEGIN x := event.x DIV T.charW; y := event.y DIV T.charH;
 END OnMouseUp;
 
 PROCEDURE RunApp*(app: App);
-VAR
-  event: G.Event;
+VAR e: G.Event;
 BEGIN
   app.quit := FALSE;
   DrawApp(app);
+  IF T.Draw() THEN G.Flip END;
   REPEAT
     G.WaitEvents(50);
-    WHILE G.PollEvent(event) DO
-      CASE event.type OF
-        G.mouseMove: OnMouseMove(app, event)
-      | G.mouseDown: OnMouseDown(app, event)
-      | G.mouseUp: OnMouseUp(app, event)
-      | G.keyDown: OnKeyDown(app, event.key)
-      | G.textInput: OnTextInput(app, event.s, event.key.sym(*!FIXME*))
+    WHILE G.PollEvent(e) DO
+      CASE e.type OF
+        G.mouseMove: OnMouseMove(app, e)
+      | G.mouseDown: OnMouseDown(app, e)
+      | G.mouseUp: OnMouseUp(app, e)
+      | G.keyDown: OnKeyDown(app, e.key)
+      | G.textInput: OnTextInput(app, e.key)
+      | G.timer: T.Act
       | G.quit: app.quit := TRUE
       ELSE
       END
     END;
-    T.Act;
     IF app.needRedraw THEN DrawApp(app) END;
     IF T.Draw() THEN G.Flip ELSE G.RepeatFlip END
   UNTIL app.quit
 END RunApp;
 
 BEGIN
-  blockToggle := FALSE;
   NEW(controlMethod); InitControlMethod(controlMethod);
   NEW(buttonMethod); InitButtonMethod(buttonMethod);
   NEW(winBtnMethod); InitWinBtnMethod(winBtnMethod);

+ 105 - 0
src/Pthread.Mod

@@ -0,0 +1,105 @@
+MODULE Pthread;
+(* Copyright 2019-2020 Arthur Yefimov
+
+This file is part of Free Oberon.
+
+Free Oberon is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Free Oberon is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+*)
+IMPORT SYSTEM;
+
+(* PROCEDURE Pr(a: Pthread.Args): Pthread.ReturnValue;
+BEGIN
+RETURN NIL END Pr;
+IF Pthread.Create(th, NIL, Pr, NIL) # 0 THEN END
+*)
+
+CONST
+  sizeOfPthread = 8;
+  sizeOfMutex = 40;
+  sizeOfCond = 48;
+
+TYPE
+  Args* = SYSTEM.PTR;
+  Attr* = SYSTEM.PTR;
+  MutexAttr* = SYSTEM.PTR;
+  CondAttr* = SYSTEM.PTR;
+  ReturnValue* = SYSTEM.PTR;
+  Thread* = RECORD
+    padding: ARRAY sizeOfPthread OF CHAR
+  END;
+  Routine* = PROCEDURE(args: Args): SYSTEM.PTR;
+
+  Mutex* = RECORD
+    padding: ARRAY sizeOfMutex OF CHAR
+  END;
+
+  Cond* = RECORD
+    padding: ARRAY sizeOfCond OF CHAR
+  END;
+
+PROCEDURE -AAIncludePthreadh0* '#include "Pthread.h0"';
+
+(* Threads *)
+
+PROCEDURE -Create*(VAR t: Thread; attr: Attr;
+  routine: Routine; args: Args): INTEGER
+  "pthread_create((pthread_t *)t, attr, routine, args)";
+
+PROCEDURE -Join*(t: Thread)
+  "pthread_join((pthread_t *)t: Thread; VAR status: SYSTEM.ADDRESS)";
+
+PROCEDURE -Detatch*(t: Thread)
+  "pthread_detatch((pthread_t *)t)";
+
+PROCEDURE -Exit*(t: Thread)
+  "pthread_detatch((pthread_t *)t)";
+
+PROCEDURE -Cancel*(t: Thread)
+  "pthread_cancel((pthread_t *)t)";
+
+(* Mutexes *)
+
+PROCEDURE -MutexInit*(VAR m: Mutex; attr: MutexAttr): INTEGER
+  "pthread_mutex_init((pthread_mutex_t *)m, attr)";
+
+PROCEDURE -MutexDestroy*(VAR m: Mutex): INTEGER
+  "pthread_mutex_destroy((pthread_mutex_t *)m)";
+
+PROCEDURE -MutexLock*(VAR m: Mutex)
+  "pthread_mutex_lock((pthread_mutex_t *)m)";
+
+PROCEDURE -MutexUnlock*(VAR m: Mutex)
+  "pthread_mutex_unlock((pthread_mutex_t *)m)";
+
+(* Condition Variables *)
+
+PROCEDURE -CondInit*(VAR c: Cond; attr: CondAttr): INTEGER
+  "pthread_cond_init((pthread_cond_t *)c, (pthread_condattr_t *)attr)";
+
+PROCEDURE -CondDestroy*(VAR c: Cond; attr: CondAttr): INTEGER
+  "pthread_cond_destroy((pthread_cond_t *)c)";
+
+PROCEDURE -CondWait*(VAR c: Cond; VAR m: Mutex): INTEGER
+  "pthread_cond_wait((pthread_cond_t *)c, (pthread_mutex_t *)m)";
+
+PROCEDURE -CondTimedWait*(VAR c: Cond; VAR m: Mutex): INTEGER
+  "pthread_cond_timedwait((pthread_cond_t *)c, (pthread_mutex_t *)m)";
+
+PROCEDURE -CondSignal*(VAR c: Cond): INTEGER
+  "pthread_cond_signal((pthread_cond_t *)c)";
+
+PROCEDURE -CondBroadcast*(VAR c: Cond): INTEGER
+  "pthread_cond_broadcast((pthread_cond_t *)c)";
+
+END Pthread.

+ 6 - 0
src/Pthread.h0

@@ -0,0 +1,6 @@
+#ifndef Pthread__h0
+#define Pthread__h0
+
+#include <pthread.h>
+
+#endif

+ 1 - 1
src/SDL2.Mod

@@ -1,5 +1,5 @@
 MODULE SDL2;
-(* Copyright 2017-2019 Arthur Yefimov
+(* Copyright 2017-2020 Arthur Yefimov
 
 This file is part of Free Oberon.
 

+ 1542 - 0
src/SDL2Graph.Mod

@@ -0,0 +1,1542 @@
+MODULE Graph;
+(* Copyright 2017-2020 Arthur Yefimov
+
+This file is part of Free Oberon.
+
+Free Oberon is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Free Oberon is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+*)
+IMPORT SDL := SDL2, (* Mix := SDL2mixer, Net := SDL2net,*)
+       SYSTEM, Platform, Out;
+
+CONST
+  (* Flip Flags *)
+  flipNone* = {};
+  flipH*    = 0;
+  flipV*    = 1;
+  flipHV*   = flipH + flipV;
+
+  (* Draw Mode Flags *)
+  drawSpriteNormal* = 0;
+  drawSpriteLit*    = 1;
+  drawSpriteTrans*  = 2;
+
+  (* Settings *)
+  fullscreen*  = 0;
+  buffered*    = 1;
+  spread*      = 2;
+  sharpPixels* = 3;
+  software*    = 4;
+  initMouse*   = 8;
+  initSound*   = 9;
+  initNet*     = 10;
+
+  (* Event Types *)
+  quit*        = 1;
+  windowEvent* = 2;
+  keyDown*     = 3;
+  keyUp*       = 4;
+  textInput*   = 5;
+  mouseMove*   = 6;
+  mouseDown*   = 7;
+  mouseUp*     = 8;
+  mouseWheel*  = 9;
+
+  (* Mouse Buttons *)
+  btnLeft*  = 0;
+  btnRight* = 1;
+  btnMid*   = 2;
+
+  (* Random Modulo *)
+  randomModulo* = 2147483647; (* =2^31-1 *)
+
+  (* Key Codes *)
+  kA*           = 4;
+  kB*           = 5;
+  kC*           = 6;
+  kD*           = 7;
+  kE*           = 8;
+  kF*           = 9;
+  kG*           = 10;
+  kH*           = 11;
+  kI*           = 12;
+  kJ*           = 13;
+  kK*           = 14;
+  kL*           = 15;
+  kM*           = 16;
+  kN*           = 17;
+  kO*           = 18;
+  kP*           = 19;
+  kQ*           = 20;
+  kR*           = 21;
+  kS*           = 22;
+  kT*           = 23;
+  kU*           = 24;
+  kV*           = 25;
+  kW*           = 26;
+  kX*           = 27;
+  kY*           = 28;
+  kZ*           = 29;
+  k1*           = 30;
+  k2*           = 31;
+  k3*           = 32;
+  k4*           = 33;
+  k5*           = 34;
+  k6*           = 35;
+  k7*           = 36;
+  k8*           = 37;
+  k9*           = 38;
+  k0*           = 39;
+  k1Pad*        = 89;
+  k2Pad*        = 90;
+  k3Pad*        = 91;
+  k4Pad*        = 92;
+  k5Pad*        = 93;
+  k6Pad*        = 94;
+  k7Pad*        = 95;
+  k8Pad*        = 96;
+  k9Pad*        = 97;
+  k0Pad*        = 98;
+  kF1*          = 58;
+  kF2*          = 59;
+  kF3*          = 60;
+  kF4*          = 61;
+  kF5*          = 62;
+  kF6*          = 63;
+  kF7*          = 64;
+  kF8*          = 65;
+  kF9*          = 66;
+  kF10*         = 67;
+  kF11*         = 68;
+  kF12*         = 69;
+  kEsc*         = 41;
+  kTilde*       = 53;
+  kMinus*       = 45;
+  kEquals*      = 46;
+  kBackspace*   = 42;
+  kTab*         = 43;
+  kOpenBrace*   = 47;
+  kCloseBrace*  = 48;
+  kEnter*       = 40;
+  kColon*       = 51;
+  kQuote*       = 52;
+  kBackslash*   = 49;
+  kBackslash2*  = 100;
+  kComma*       = 54;
+  kStop*        = 55;
+  kSlash*       = 56;
+  kSpace*       = 44;
+  kInsert*      = 73;
+  kDel*         = 76;
+  kHome*        = 74;
+  kEnd*         = 77;
+  kPgUp*        = 75;
+  kPgDn*        = 78;
+  kLeft*        = 80;
+  kRight*       = 79;
+  kUp*          = 82;
+  kDown*        = 81;
+  kSlashPad*    = 84;
+  kAsterisk*    = 85;
+  kMinusPad*    = 86;
+  kPlusPad*     = 87;
+  kDelPad*      = 99;
+  kEnterPad*    = 88;
+  kPrtScr*      = 70;
+  kPause*       = 72;
+
+  kModifiers*   = 115;
+
+  kLShift*      = 225;
+  kRShift*      = 229;
+  kLCtrl*       = 224;
+  kRCtrl*       = 228;
+  kAlt*         = 226;
+  kAltGr*       = 230;
+  kLWin*        = 227;
+  kRWin*        = 231;
+  kMenu*        = 123;
+  kScrLock*     = 124;
+  kNumLock*     = 125;
+  kCapsLock*    = 126;
+
+  kMax*         = 127;
+
+  (* Modifiers Set *)
+  mLShift*   = 0;
+  mRShift*   = 1;
+  mLCtrl*    = 6;
+  mRCtrl*    = 7;
+  mLAlt*     = 8;
+  mRAlt*     = 9;
+  mLGui*     = 10;
+  mRGui*     = 11;
+  mNum*      = 12;
+  mCaps*     = 13;
+  mMode*     = 14;
+  mReserved* = 15;
+  mCtrl*  = {mLCtrl, mRCtrl};
+  mShift* = {mLShift, mRShift};
+  mAlt*   = {mLAlt, mRAlt};
+  mGui*   = {mLGui, mRGui};
+
+TYPE
+  SET32 = SYSTEM.SET32;
+
+  Bitmap* = POINTER TO BitmapDesc;
+  BitmapDesc* = RECORD
+    surface: SDL.Surface;
+    w*, h*: INTEGER
+  END;
+
+  Font* = POINTER TO FontDesc;
+  FontDesc* = RECORD
+    bmp*: Bitmap;
+    charW*, charH*: INTEGER;
+    charRows*, charsInRow*: INTEGER;
+    sprites*: POINTER [1] TO ARRAY OF ARRAY OF SDL.Rect
+  END;
+
+  KeyArray = SDL.KeyArray;
+
+  Key* = RECORD
+    code*: INTEGER; (* Physical key code *)
+    sym*: INTEGER; (* Virtual key code *)
+    mod*: SET; (* Key modifiers *)
+    repeat*: BOOLEAN
+  END;
+
+  (*
+  Sample* = POINTER TO SampleDesc;
+  SampleDesc* = RECORD
+    chunk*: Mix.Chunk
+  END;
+
+  Music* = POINTER TO MusicDesc;
+  MusicDesc* = RECORD
+    music*: Mix.Music
+  END;
+  *)
+
+  Region* = RECORD
+    x*, y*, w*, h*: INTEGER
+  END;
+
+  (* SDL Net *)
+  (*
+  IPAddress* = Net.IPAddress;
+  Socket* = Net.TCPSocket;
+
+  SocketSet* = Net.SocketSet;
+
+  PStr* = POINTER TO ARRAY OF CHAR;
+
+  NetBuf* = POINTER TO NetBufDesc;
+  NetBufDesc* = RECORD
+    s: PStr;
+    len: INTEGER; (* Actual used length of the buffer *)
+    lastLen: INTEGER
+  END;
+  *)
+
+  Event* = RECORD
+    type*: INTEGER;
+    key*: Key;
+    x*, y*: INTEGER;
+    xRel*, yRel*: INTEGER;
+    button*: INTEGER;
+    buttons*: SET; (* What mouse buttons are pressed *)
+    down*: BOOLEAN;
+    s*: ARRAY 32 OF CHAR
+  END;
+
+  EventQueue* = RECORD
+    buf: ARRAY 256 OF Event;
+    first, last: INTEGER; (* Index of first and last element *)
+    len: INTEGER (* Amount of elements currently in queue *)
+  END;
+
+  CloseBtnProc* = PROCEDURE;
+
+VAR
+  window: SDL.Window;
+  renderer: SDL.Renderer;
+  screen: Bitmap;
+  screenTexture: SDL.Texture;
+  events: EventQueue;
+  keyPressed: INTEGER;
+
+  sizeStepX, sizeStepY: INTEGER;
+  wantW, wantH: INTEGER;
+  wantFullscreen, wantBuffer, wantSpread: BOOLEAN;
+  wantSharpPixels, wantSoftware: BOOLEAN;
+  wantMouse, wantSound, wantNet: BOOLEAN;
+  wantFPS: INTEGER;
+  buffer: Bitmap;
+  lastFlip: INTEGER;
+  frames, framesT: INTEGER;
+
+  (* Flip Region *)
+  flipRegion: Region;
+
+  (* Mouse *)
+  mouseX, mouseY: INTEGER;
+  mouseFocusX, mouseFocusY: INTEGER;
+  lastBlitMouseOutside: BOOLEAN;
+  lastBlitMouseX, lastBlitMouseY: INTEGER;
+  needRedrawMouse: BOOLEAN; (* True if mouse has moved since last redraw *)
+  showMouse: BOOLEAN; (* Whether to show mouse pointer on screen *)
+  stdMousePointer: Bitmap;
+  mousePointer: Bitmap;
+  underMouse: Bitmap; (* Buffer to copy part of image under the mouse *)
+
+  randomSeed-: INTEGER;
+
+PROCEDURE -AAIncludeSDL2h0 '#include "SDL2.h0"';
+
+(* C Macros *)
+
+PROCEDURE LoadBmp(filename: ARRAY OF CHAR): SDL.Surface;
+BEGIN
+  RETURN SDL.LoadBmpRW(SDL.RWFromFile(filename, 'rb'), 1)
+END LoadBmp;
+
+(* Misc *)
+
+PROCEDURE Str*(n: INTEGER; VAR s: ARRAY OF CHAR);
+BEGIN
+  (*IntStr.IntToStr(n, s)*)
+  s[0] := '4'; s[1] := '2'; s[2] := 0X (*!FIXME*)
+END Str;
+
+PROCEDURE Val*(s: ARRAY OF CHAR): INTEGER;
+VAR n: INTEGER; (*res: IntStr.ConvResults;*)
+BEGIN
+  (*IntStr.StrToInt(s, n, res);
+  IF res # IntStr.strAllRight THEN n := 0 END;
+  RETURN n*)
+  RETURN 42 (*!FIXME*)
+END Val;
+
+PROCEDURE AppendN*(n: INTEGER; VAR s: ARRAY OF CHAR);
+VAR sn: ARRAY 30 OF CHAR;
+BEGIN
+  (*Str(n, sn); S.Append(sn, s) !FIXME *)
+END AppendN;
+
+(* General *)
+
+PROCEDURE GetError*(VAR s: ARRAY OF CHAR);
+TYPE P = POINTER TO ARRAY 10240 OF CHAR;
+VAR p: P;
+BEGIN
+  p := SYSTEM.VAL(P, SDL.GetError());
+  COPY(p^, s)
+END GetError;
+
+PROCEDURE Settings*(w, h: INTEGER; flags: SET);
+BEGIN (*!TODO: Refactor, save options in a SET*)
+  wantW := w; wantH := h;
+  wantFullscreen := fullscreen IN flags;
+  wantBuffer := buffered IN flags;
+  wantSpread := spread IN flags;
+  wantSharpPixels := sharpPixels IN flags;
+  wantSoftware := software IN flags;
+  wantMouse := initMouse IN flags;
+  showMouse := wantMouse;
+  wantNet := initNet IN flags;
+  wantSound := initSound IN flags
+END Settings;
+
+PROCEDURE SetSizeStep*(w, h: INTEGER);
+BEGIN
+  sizeStepX := w; sizeStepY := h
+END SetSizeStep;
+
+PROCEDURE SetFPS*(fps: INTEGER);
+BEGIN
+  IF fps <= 0 THEN fps := -1 END;
+  wantFPS := fps
+END SetFPS;
+
+PROCEDURE GetDesktopResolution*(VAR w, h: INTEGER);
+VAR mode: SDL.DisplayMode;
+BEGIN
+  SDL.GetDesktopDisplayMode(0, mode);
+  w := mode.w; h := mode.h
+END GetDesktopResolution;
+
+PROCEDURE CheckWantedScreenSize;
+BEGIN
+  IF (wantW = -1) OR (wantH = -1) THEN
+    GetDesktopResolution(wantW, wantH);
+    IF ~wantFullscreen THEN
+      DEC(wantW, 20); DEC(wantH, 50)
+    END
+  END
+END CheckWantedScreenSize;
+
+(* Flip Region *)
+PROCEDURE SetRegion*(x, y, w, h: INTEGER);
+BEGIN
+  flipRegion.x := x;  flipRegion.y := y;
+  flipRegion.w := w;  flipRegion.h := h
+END SetRegion;
+
+PROCEDURE UnsetRegion*;
+BEGIN
+  flipRegion.w := -1
+END UnsetRegion;
+
+PROCEDURE AddRegion*(x, y, w, h: INTEGER);
+BEGIN
+  IF flipRegion.w = -1 THEN (* No flip region yet *)
+    SetRegion(x, y, w, h) (* Just set it *)
+  ELSE (* Flip Region exists, add to it *)
+    IF x < flipRegion.x THEN flipRegion.x := x END;
+    IF y < flipRegion.y THEN flipRegion.y := y END;
+    IF x + w > flipRegion.x + flipRegion.w THEN
+      flipRegion.w := w + x - flipRegion.x END;
+    IF y + h > flipRegion.y + flipRegion.h THEN
+      flipRegion.h := h + y - flipRegion.y END
+  END
+END AddRegion;
+
+(* Drawing *)
+
+PROCEDURE MakeCol*(r, g, b: INTEGER): INTEGER;
+BEGIN
+  r := SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET32, r) * {0..7});
+  g := SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET32, g) * {0..7});
+  b := SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET32, b) * {0..7});
+  RETURN SYSTEM.LSH(SYSTEM.LSH(0FF00H + b, 8) + g, 8) + r
+END MakeCol;
+
+PROCEDURE ColorToRGB*(color: INTEGER; VAR r, g, b: INTEGER);
+BEGIN
+  r := SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET32, color) * {0..7});
+  g := SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET32, SYSTEM.LSH(color, -8)) * {0..7});
+  b := SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET32, SYSTEM.LSH(color, -16)) * {0..7})
+END ColorToRGB;
+
+PROCEDURE BmpCol*(bmp: Bitmap; r, g, b: INTEGER): INTEGER;
+BEGIN
+  RETURN SDL.MapRGB(bmp.surface.format, SHORT(r), SHORT(g), SHORT(b))
+END BmpCol;
+
+PROCEDURE ClearToColor*(bmp: Bitmap; color: INTEGER);
+BEGIN
+  SDL.FillRectNil(bmp.surface, color)
+END ClearToColor;
+
+PROCEDURE ClearBitmap*(bmp: Bitmap);
+BEGIN
+  ClearToColor(bmp, MakeCol(0, 0, 0))
+END ClearBitmap;
+
+PROCEDURE ClearScreenToColor*(color: INTEGER);
+BEGIN
+  ClearToColor(screen, color)
+END ClearScreenToColor;
+
+PROCEDURE ClearScreen*;
+BEGIN
+  ClearToColor(screen, MakeCol(0, 0, 0))
+END ClearScreen;
+
+PROCEDURE LockBitmap*(bmp: Bitmap);
+BEGIN
+  SDL.LockSurface(bmp.surface)
+END LockBitmap;
+
+PROCEDURE UnlockBitmap*(bmp: Bitmap);
+BEGIN
+  SDL.UnlockSurface(bmp.surface)
+END UnlockBitmap;
+
+PROCEDURE PutPixelFast*(bmp: Bitmap; x, y, color: INTEGER);
+VAR n: SYSTEM.ADDRESS;
+BEGIN n := SYSTEM.VAL(SYSTEM.ADDRESS, bmp.surface.pixels);
+  INC(n, (y * bmp.w + x) * 4);
+  SYSTEM.PUT(n, color)
+END PutPixelFast;
+
+PROCEDURE PutPixel*(bmp: Bitmap; x, y, color: INTEGER);
+VAR n: SYSTEM.ADDRESS;
+BEGIN
+  IF (x >= 0) & (x < bmp.w) &
+     (y >= 0) & (y < bmp.h) THEN
+    SDL.LockSurface(bmp.surface);
+    n := SYSTEM.VAL(SYSTEM.ADDRESS, bmp.surface.pixels);
+    INC(n, (y * bmp.w + x) * 4);
+    SYSTEM.PUT(n, color);
+    SDL.UnlockSurface(bmp.surface)
+  END
+END PutPixel;
+
+PROCEDURE GetPixel*(bmp: Bitmap; x, y: INTEGER): INTEGER;
+VAR color: INTEGER;
+  n: SYSTEM.ADDRESS;
+BEGIN
+  IF (x >= 0) & (x < bmp.w) &
+     (y >= 0) & (y < bmp.h) THEN
+    SDL.LockSurface(bmp.surface);
+    n := SYSTEM.VAL(SYSTEM.ADDRESS, bmp.surface.pixels);
+    INC(n, (y * bmp.w + x) * 4);
+    SYSTEM.GET(n, color);
+    SDL.UnlockSurface(bmp.surface)
+  ELSE color := 0 END;
+  RETURN color
+END GetPixel;
+
+PROCEDURE HLine*(bmp: Bitmap; x1, y, x2, color: INTEGER);
+VAR rect: SDL.Rect; t: INTEGER;
+BEGIN
+  IF x1 > x2 THEN t := x1; x1 := x2; x2 := t END;
+  rect.x := x1; rect.y := y;
+  rect.w := x2 - x1 + 1; rect.h := 1;
+  SDL.FillRect(bmp.surface, rect, color)
+END HLine;
+
+PROCEDURE VLine*(bmp: Bitmap; x, y1, y2, color: INTEGER);
+VAR rect: SDL.Rect; t: INTEGER;
+BEGIN
+  IF y1 > y2 THEN t := y1; y1 := y2; y2 := t END;
+  rect.x := x; rect.y := y1;
+  rect.w := 1; rect.h := y2 - y1 + 1;
+  SDL.FillRect(bmp.surface, rect, color)
+END VLine;
+
+PROCEDURE Line*(bmp: Bitmap; x1, y1, x2, y2, color: INTEGER);
+VAR x, y, i, dx, dy, sx, sy, e: INTEGER; vert: BOOLEAN;
+BEGIN
+  IF x1 = x2 THEN VLine(bmp, x1, y1, y2, color)
+  ELSIF y1 = y2 THEN HLine(bmp, x1, y1, x2, color)
+  ELSE
+    SDL.LockSurface(bmp.surface);
+    dx := ABS(x1 - x2); dy := ABS(y1 - y2);
+    IF x2 > x1 THEN sx := 1 ELSE sx := -1 END;
+    IF y2 > y1 THEN sy := 1 ELSE sy := -1 END;
+    x := x1; y := y1; vert := dy > dx;
+    IF vert THEN i := dx; dx := dy; dy := i END;
+    e := 2 * dy - dx;
+    FOR i := 0 TO dx DO
+      IF (x >= 0) & (x < bmp.w) &
+         (y >= 0) & (y < bmp.h) THEN
+        PutPixelFast(bmp, x, y, color)
+      END;
+      IF e >= 0 THEN
+        IF vert THEN INC(x, sx) ELSE INC(y, sy) END;
+        DEC(e, 2 * dx)
+      END;
+      IF vert THEN INC(y, sy) ELSE INC(x, sx) END;
+      INC(e, 2 * dy)
+    END;
+    SDL.UnlockSurface(bmp.surface)
+  END
+END Line;
+
+PROCEDURE FastLine*(bmp: Bitmap; x1, y1, x2, y2, color: INTEGER);
+BEGIN
+  (*Al.FastLine(bmp.bmp, x1, y1, x2, y2, color)*)
+END FastLine;
+
+PROCEDURE Rect*(bmp: Bitmap; x1, y1, x2, y2, color: INTEGER); (*!FIXME*)
+VAR rect: SDL.Rect;
+BEGIN
+  rect.x := x1; rect.y := y1;
+  rect.w := 1; rect.h := y2 - y1 + 1;
+  SDL.FillRect(bmp.surface, rect, color);
+  rect.x := x2;
+  SDL.FillRect(bmp.surface, rect, color);
+  rect.x := x1; rect.w := x2 - x1 + 1; rect.h := 1;
+  SDL.FillRect(bmp.surface, rect, color);
+  rect.y := y2;
+  SDL.FillRect(bmp.surface, rect, color)
+END Rect;
+
+PROCEDURE RectFill*(bmp: Bitmap; x1, y1, x2, y2, color: INTEGER);
+VAR rect: SDL.Rect;
+BEGIN
+  rect.x := x1; rect.y := y1;
+  rect.w := x2 - x1 + 1; rect.h := y2 - y1 + 1;
+  SDL.FillRect(bmp.surface, rect, color)
+END RectFill;
+
+PROCEDURE Circle*(b: Bitmap; cx, cy, r, col: INTEGER);
+VAR x, y, d: INTEGER;
+BEGIN
+  x := 0; y := r; d := 3 - 2 * r;
+  WHILE x <= y DO
+    PutPixel(b, cx + x, cy + y, col);
+    PutPixel(b, cx + y, cy + x, col);
+    PutPixel(b, cx + x, cy - y, col);
+    PutPixel(b, cx + y, cy - x, col);
+    PutPixel(b, cx - x, cy + y, col);
+    PutPixel(b, cx - y, cy + x, col);
+    PutPixel(b, cx - x, cy - y, col);
+    PutPixel(b, cx - y, cy - x, col);
+    IF d < 0 THEN d := d + 4 * x + 6
+    ELSE d := d + 4 * (x - y) + 10; DEC(y)
+    END;
+    INC(x)
+  END
+END Circle;
+
+PROCEDURE CircleFill*(b: Bitmap; cx, cy, r, col: INTEGER);
+VAR x, y, d: INTEGER;
+BEGIN
+  x := 0; y := r; d := 3 - 2 * r;
+  WHILE x <= y DO
+    HLine(b, cx - x, cy + y, cx + x, col);
+    HLine(b, cx - y, cy + x, cx + y, col);
+    HLine(b, cx - x, cy - y, cx + x, col);
+    HLine(b, cx - y, cy - x, cx + y, col);
+    IF d < 0 THEN d := d + 4 * x + 6
+    ELSE d := d + 4 * (x - y) + 10; DEC(y)
+    END;
+    INC(x)
+  END
+END CircleFill;
+
+PROCEDURE Ellipse*(bmp: Bitmap; x, y, rx, ry, color: INTEGER);
+BEGIN
+END Ellipse;
+
+PROCEDURE EllipseFill*(bmp: Bitmap; x, y, rx, ry, color: INTEGER);
+BEGIN
+END EllipseFill;
+
+PROCEDURE FloodFill*(bmp: Bitmap; x, y, color: INTEGER);
+BEGIN
+END FloodFill;
+
+(* Bitmap *)
+
+PROCEDURE (bmp: Bitmap) Finalize*;
+BEGIN
+END Finalize;
+
+PROCEDURE CreateBitmap*(w, h: INTEGER): Bitmap;
+VAR bmp: Bitmap;
+    s: ARRAY 2560 OF CHAR;
+BEGIN
+  NEW(bmp);
+  bmp.surface := SDL.CreateRGBSurface(0, w, h, 32,
+    000000FFH, 0000FF00H, 00FF0000H, -1000000H);
+  IF bmp.surface = NIL THEN
+    GetError(s); Out.String(s); Out.Ln
+  END;
+  bmp.w := w;  bmp.h := h;
+  RETURN bmp
+END CreateBitmap;
+
+PROCEDURE DestroyBitmap*(bmp: Bitmap);
+BEGIN
+  SDL.FreeSurface(bmp.surface)
+END DestroyBitmap;
+
+PROCEDURE LoadBitmap*(filename: ARRAY OF CHAR): Bitmap;
+VAR bmp: Bitmap;
+BEGIN        (* LoadBmp(filename) for BMP-only *)
+  NEW(bmp); bmp.surface := SDL.ImgLoad(filename);
+  IF bmp.surface = NIL THEN bmp := NIL
+  ELSE bmp.w := bmp.surface.w; bmp.h := bmp.surface.h END;
+  RETURN bmp
+END LoadBitmap;
+
+PROCEDURE Blit*(src, dest: Bitmap; sx, sy, sw, sh, dx, dy: INTEGER);
+VAR a, b: SDL.Rect;
+BEGIN a.x := sx; a.y := sy; a.w := sw; a.h := sh;
+  b.x := dx; b.y := dy;
+  SDL.BlitSurface(src.surface, a, dest.surface, b)
+END Blit;
+
+PROCEDURE BlitWhole*(src, dest: Bitmap; x, y: INTEGER);
+VAR b: SDL.Rect;
+BEGIN b.x := x; b.y := y;
+  SDL.BlitSurfaceNil(src.surface, dest.surface, b)
+END BlitWhole;
+
+PROCEDURE StretchBlit*(src, dest: Bitmap; sx, sy, sw, sh, dx, dy, dw, dh: INTEGER);
+VAR a, b: SDL.Rect;
+BEGIN
+  a.x := sx; a.y := sy; a.w := sw; a.h := sh;
+  b.x := dx; b.y := dy; b.w := dw; b.h := dh;
+  SDL.BlitScaled(src.surface, a, dest.surface, b)
+END StretchBlit;
+
+PROCEDURE SetAlpha*(bmp: Bitmap; alpha: INTEGER);
+BEGIN
+  IF SDL.SetSurfaceAlphaMod(bmp.surface, CHR(alpha)) = 0 THEN END
+END SetAlpha;
+
+PROCEDURE MaskedBlit*(src, dest: Bitmap; sx, sy, dx, dy, w, h: INTEGER);
+BEGIN
+  (*Al.MaskedBlit(src.bmp, dest.bmp, sx, sy, dx, dy, w, h)*)
+END MaskedBlit;
+
+PROCEDURE DrawSpriteEx*(dest, sprite: Bitmap; x, y, mode: INTEGER; flip: SET);
+BEGIN
+  (*Al.DrawSpriteEx(dest.bmp, sprite.bmp, x, y, mode, flip)*)
+END DrawSpriteEx;
+
+PROCEDURE DrawCharacterEx*(dest, sprite: Bitmap; x, y, color, bg: INTEGER);
+BEGIN
+  (*Al.DrawCharacterEx(dest.bmp, sprite.bmp, x, y, color, bg)*)
+END DrawCharacterEx;
+
+PROCEDURE SetColorKey*(bmp: Bitmap; color: INTEGER);
+BEGIN
+  SDL.SetColorKey(bmp.surface, 1, color)
+END SetColorKey;
+
+(* Font *)
+
+PROCEDURE LoadFont*(filename: ARRAY OF CHAR; charW, charH: INTEGER): Font;
+VAR bmp: Bitmap; font: Font;
+    x, y, sx, sy, tmp: INTEGER;
+BEGIN
+  bmp := LoadBitmap(filename);
+  IF bmp = NIL THEN font := NIL
+  ELSE
+    bmp.surface := SDL.ConvertSurface(bmp.surface,
+      screen.surface.format, 0);
+    SetColorKey(bmp, BmpCol(bmp, 0, 0, 0));
+    NEW(font); font.bmp := bmp;
+    font.charW := charW; font.charH := charH;
+    font.charsInRow := font.bmp.w DIV charW;
+    font.charRows := font.bmp.h DIV charH;
+    (*!FIXME remove sprites from here at all*)
+    NEW(font.sprites, font.charRows, font.charsInRow);
+    sy := 0;
+    FOR y := 0 TO font.charRows - 1 DO
+      sx := 0;
+      FOR x := 0 TO font.charsInRow - 1 DO
+        font.sprites[y, x].x := sx;
+        font.sprites[y, x].y := sy;
+        font.sprites[y, x].w := charW;
+        font.sprites[y, x].h := charH;
+        INC(sx, charW)
+      END;
+      INC(sy, charH)
+    END
+  END;
+  RETURN font
+END LoadFont;
+
+PROCEDURE DrawCharacter*(dest: Bitmap; font: Font;
+  x, y: INTEGER; ch: CHAR; fg: INTEGER);
+VAR fx, fy, r, g, b: INTEGER; dstRect: SDL.Rect;
+BEGIN
+  dstRect.x := x; dstRect.y := y;
+  fx := ORD(ch) MOD font.charsInRow;
+  fy := ORD(ch) DIV font.charsInRow;
+  ColorToRGB(fg, r, g, b);
+  SDL.SetSurfaceColorMod(font.bmp.surface, r, g, b);
+  SDL.BlitSurface(font.bmp.surface, font.sprites[fy, fx],
+    screen.surface, dstRect)
+END DrawCharacter;
+
+PROCEDURE DrawString*(dest: Bitmap; font: Font;
+  x, y: INTEGER; s: ARRAY OF CHAR; fg: INTEGER);
+VAR i, cx: INTEGER;
+BEGIN
+  i := 0; cx := x;
+  WHILE (s[i] # 0X) & (cx < dest.w) DO
+    DrawCharacter(dest, font, cx, y, s[i], fg);
+    INC(i); INC(cx, font.charW)
+  END
+END DrawString;
+
+PROCEDURE StartTextInput*;
+BEGIN SDL.StartTextInput
+END StartTextInput;
+
+PROCEDURE StopTextInput*;
+BEGIN SDL.StopTextInput
+END StopTextInput;
+
+PROCEDURE QueueEvent;
+BEGIN
+  INC(events.len); INC(events.last);
+  IF events.last = LEN(events.buf) THEN events.last := 0 END
+END QueueEvent;
+
+PROCEDURE PumpKeyDown(VAR event: SDL.Event);
+VAR e: SDL.KeyboardEvent;
+  n: INTEGER; mod: SET;
+BEGIN
+  IF events.len < LEN(events.buf) THEN
+    e := SYSTEM.VAL(SDL.KeyboardEvent, SYSTEM.ADR(event));
+    n := e.keysym.mod; mod := SYSTEM.VAL(SET32, n);
+    QueueEvent;
+    events.buf[events.last].type := keyDown;
+    events.buf[events.last].key.code := e.keysym.scancode;
+    events.buf[events.last].key.sym := e.keysym.sym;
+    events.buf[events.last].key.mod := mod;
+    events.buf[events.last].key.repeat := e.repeat # 0;
+    INC(keyPressed)
+  END
+END PumpKeyDown;
+
+PROCEDURE PumpTextEvent(event: SDL.Event);
+VAR sym: INTEGER;
+    e: SDL.TextInputEvent;
+BEGIN
+  IF events.len < LEN(events.buf) THEN
+    e := SYSTEM.VAL(SDL.TextInputEvent, SYSTEM.ADR(event));
+    IF e.text[1] = 0X THEN (* ASCII character *)
+      sym := ORD(e.text[0])
+    ELSE (* Unicode character. Assuming 2 bytes *)
+      sym := ORD(e.text[1]);
+      (* UTF-8 cyrillic *)
+      IF (e.text[0] = 0D0X) OR (e.text[0] = 0D1X) THEN
+        IF e.text[0] = 0D0X THEN DEC(sym, 090H)
+        ELSE DEC(sym, 060H - 16)
+        END;
+        (* Convert to CP866 *)
+        IF sym = 65 THEN sym := 0F1H (* jo *)
+        ELSIF sym = -15 THEN sym := 0F0H (* JO *)
+        ELSIF sym < 48 THEN INC(sym, 80H) (* A..JA, a..p *)
+        ELSE INC(sym, 0E0H - 48) (* r..ja *)
+        END
+      END
+    END;
+    QueueEvent;
+    events.buf[events.last].type := textInput;
+    events.buf[events.last].key.sym := sym; (*!FIXME sym needed?*)
+    COPY(e.text, events.buf[events.last].s)
+  END
+END PumpTextEvent;
+
+PROCEDURE UpdateMousePos(event: SDL.Event);
+VAR e: SDL.MouseMotionEvent; newX, newY: INTEGER;
+BEGIN
+  e := SYSTEM.VAL(SDL.MouseMotionEvent, SYSTEM.ADR(event));
+  newX := e.x; newY := e.y;
+  IF newX < 0 THEN newX := 0
+  ELSIF newX >= screen.w THEN newX := screen.w - 1 END;
+  IF newY < 0 THEN newY := 0
+  ELSIF newY >= screen.h THEN newY := screen.h - 1 END;
+  IF (newX # mouseX) OR (newY # mouseY) THEN
+    mouseX := newX;  mouseY := newY;
+    needRedrawMouse := TRUE
+  END
+END UpdateMousePos;
+
+(* Keyboard *)
+
+PROCEDURE GetKeyArray(): KeyArray;
+BEGIN
+  RETURN SYSTEM.VAL(KeyArray, SDL.GetKeyboardStateNil())
+END GetKeyArray;
+
+PROCEDURE KeyDown*(key: INTEGER): BOOLEAN;
+VAR keys: KeyArray;
+BEGIN
+  keys := GetKeyArray();
+  RETURN keys[key]
+END KeyDown;
+
+PROCEDURE AltPressed*(): BOOLEAN;
+VAR keys: KeyArray;
+BEGIN
+  keys := GetKeyArray();
+  RETURN keys[kAlt] OR keys[kAltGr]
+END AltPressed;
+
+PROCEDURE ShiftPressed*(): BOOLEAN;
+VAR keys: KeyArray;
+BEGIN
+  keys := GetKeyArray();
+  RETURN keys[kLShift] OR keys[kRShift]
+END ShiftPressed;
+
+PROCEDURE CtrlPressed*(): BOOLEAN;
+VAR keys: KeyArray;
+BEGIN
+  keys := GetKeyArray();
+  RETURN keys[kLCtrl] OR keys[kRCtrl]
+END CtrlPressed;
+
+(* Mouse *)
+
+PROCEDURE MouseOnScreen*(): BOOLEAN;
+VAR flags: SET;
+BEGIN
+  flags := SDL.GetWindowFlags(window);
+  RETURN SDL.windowMouseFocus IN flags
+END MouseOnScreen;
+
+PROCEDURE ShowMouse*(show: BOOLEAN);
+BEGIN
+  showMouse := show
+END ShowMouse;
+
+PROCEDURE GetRealMousePos*(VAR x, y: INTEGER);
+BEGIN
+  IF SDL.GetMouseState(x, y) = 0 THEN END
+END GetRealMousePos;
+
+PROCEDURE GetMousePos*(VAR x, y: INTEGER);
+BEGIN
+  x := mouseX; y := mouseY
+END GetMousePos;
+
+PROCEDURE GetMouseButtons*(): SET;
+VAR x, y: INTEGER;
+BEGIN RETURN SYSTEM.VAL(SET32, SDL.GetMouseState(x, y))
+END GetMouseButtons;
+
+PROCEDURE CreateStdMousePointer*;
+VAR b: Bitmap; fg, bg: INTEGER;
+BEGIN b := CreateBitmap(12, 19);
+  bg := MakeCol(255, 0, 255); fg := MakeCol(0, 0, 0);
+  ClearToColor(b, bg); SetColorKey(b, bg);
+  Line(b, 0, 0, 10, 10, fg); Line(b, 0, 0, 0, 14, fg);
+  Line(b, 0, 14, 3, 11, fg); Line(b, 10, 10, 6, 10, fg);
+  Line(b, 4, 12, 6, 17, fg); Line(b, 6, 11, 9, 17, fg);
+  Line(b, 7, 18, 8, 18, fg); bg := MakeCol(255, 255, 255);
+  VLine(b, 1, 2, 12, bg); VLine(b, 2, 3, 11, bg);
+  VLine(b, 3, 4, 10, bg); VLine(b, 4, 5, 11, bg);
+  VLine(b, 5, 6, 13, bg); VLine(b, 6, 7, 9, bg);
+  VLine(b, 7, 8, 9, bg); VLine(b, 8, 9, 9, bg);
+  VLine(b, 6, 12, 15, bg); VLine(b, 7, 14, 17, bg);
+  VLine(b, 8, 16, 17, bg);
+  stdMousePointer := b
+END CreateStdMousePointer;
+
+PROCEDURE SetMouseFocus*(x, y: INTEGER);
+BEGIN
+  mouseFocusX := x; mouseFocusY := y;
+  needRedrawMouse := TRUE
+END SetMouseFocus;
+
+PROCEDURE SetMousePointer*(bmp: Bitmap; x, y: INTEGER);
+BEGIN
+  IF bmp = NIL THEN
+    mousePointer := stdMousePointer;
+    x := 0; y := 0
+  ELSE mousePointer := bmp
+  END;
+  SetMouseFocus(x, y);
+  underMouse := CreateBitmap(mousePointer.w, mousePointer.h);
+  needRedrawMouse := TRUE
+END SetMousePointer;
+
+PROCEDURE GetMousePointer*(): Bitmap;
+BEGIN
+  RETURN mousePointer
+END GetMousePointer;
+
+PROCEDURE SetStdMousePointer*;
+BEGIN
+  SetMousePointer(stdMousePointer, 0, 0)
+END SetStdMousePointer;
+
+PROCEDURE InitMouseData;
+BEGIN
+  CreateStdMousePointer;
+  SetStdMousePointer
+END InitMouseData;
+
+(* Misc *)
+PROCEDURE SetWindowTitle*(title: ARRAY OF CHAR);
+BEGIN
+  SDL.SetWindowTitle(window, title)
+END SetWindowTitle;
+
+PROCEDURE SwitchToWindowed*;
+BEGIN
+  IF wantFullscreen THEN
+    SDL.SetWindowSize(window, screen.w, screen.h);
+    IF SDL.SetWindowFullscreen(window, {}) = 0 THEN
+      wantFullscreen := FALSE
+    END
+  END
+END SwitchToWindowed;
+
+PROCEDURE SwitchToFullscreen*;
+BEGIN
+  IF ~wantFullscreen THEN
+    IF SDL.SetWindowFullscreen(window, SDL.windowFullscreenDesktop) = 0 THEN
+      wantFullscreen := TRUE
+    END
+  END
+END SwitchToFullscreen;
+
+PROCEDURE ToggleFullscreen*;
+BEGIN
+  IF wantFullscreen THEN SwitchToWindowed
+  ELSE SwitchToFullscreen
+  END
+END ToggleFullscreen;
+
+PROCEDURE Delay*(n: INTEGER);
+BEGIN
+  SDL.Delay(n)
+END Delay;
+
+PROCEDURE HandleMouseButton(VAR event: SDL.Event);
+VAR e: SDL.MouseButtonEvent;
+BEGIN
+
+END HandleMouseButton;
+
+PROCEDURE PumpQuit;
+BEGIN
+  IF events.len < LEN(events.buf) THEN
+    QueueEvent;
+    events.buf[events.last].type := quit
+  END
+END PumpQuit;
+
+PROCEDURE PumpMouseMove(VAR event: SDL.Event);
+VAR e: SDL.MouseMotionEvent;
+    newX, newY: INTEGER;
+BEGIN
+  e := SYSTEM.VAL(SDL.MouseMotionEvent, SYSTEM.ADR(event));
+  newX := e.x; newY := e.y;
+  IF newX < 0 THEN newX := 0
+  ELSIF newX >= screen.w THEN newX := screen.w - 1
+  END;
+  IF newY < 0 THEN newY := 0
+  ELSIF newY >= screen.h THEN newY := screen.h - 1
+  END;
+  IF (newX # mouseX) OR (newY # mouseY) THEN
+    mouseX := newX; mouseY := newY;
+    needRedrawMouse := TRUE;
+    IF events.len < LEN(events.buf) THEN
+      QueueEvent;
+      events.buf[events.last].type := mouseMove;
+      events.buf[events.last].x := newX;
+      events.buf[events.last].y := newY;
+      events.buf[events.last].xRel := e.xRel;
+      events.buf[events.last].yRel := e.yRel;
+      events.buf[events.last].buttons := SYSTEM.VAL(SET32, e.state)
+    END
+  END
+END PumpMouseMove;
+
+PROCEDURE PumpMouseButton(VAR event: SDL.Event; type: INTEGER);
+VAR e: SDL.MouseButtonEvent;
+BEGIN
+  e := SYSTEM.VAL(SDL.MouseButtonEvent, SYSTEM.ADR(event));
+  IF events.len < LEN(events.buf) THEN
+    QueueEvent;
+    events.buf[events.last].type := type;
+    events.buf[events.last].button := e.button - 1;
+    events.buf[events.last].down := e.state # 0;
+    IF e.x < 0 THEN e.x := 0
+    ELSIF e.x >= screen.w THEN e.x := screen.w - 1
+    END;
+    IF e.y < 0 THEN e.y := 0
+    ELSIF e.y >= screen.h THEN e.y := screen.h - 1
+    END;
+    events.buf[events.last].x := e.x;
+    events.buf[events.last].y := e.y
+  END
+END PumpMouseButton;
+
+PROCEDURE RepeatFlip*;
+BEGIN
+  IF screenTexture # NIL THEN
+    SDL.SetRenderDrawColor(renderer, 0, 0, 0, 255);
+    SDL.RenderClear(renderer);
+    SDL.RenderCopyNil(renderer, screenTexture);
+    SDL.RenderPresent(renderer)
+  END
+END RepeatFlip;
+
+PROCEDURE WaitEvents*(timeout: INTEGER);
+VAR event: SDL.Event; n: INTEGER;
+BEGIN
+  n := SDL.PollEvent(event);
+  IF (n # 0) OR (events.len = 0) THEN
+    IF n = 0 THEN
+      IF timeout > 0 THEN n := SDL.WaitEventTimeout(event, timeout)
+      ELSIF timeout < 0 THEN n := SDL.WaitEvent(event)
+      END
+    END;
+    IF n # 0 THEN
+      REPEAT
+        IF event.type = SDL.mouseMotion THEN
+          PumpMouseMove(event)
+        ELSIF event.type = SDL.mouseButtonDown THEN
+          PumpMouseButton(event, mouseDown)
+        ELSIF event.type = SDL.mouseButtonUp THEN
+          PumpMouseButton(event, mouseUp)
+        ELSIF event.type = SDL.keyDown THEN
+          PumpKeyDown(event)
+        ELSIF event.type = SDL.textInput THEN
+          PumpTextEvent(event)
+        ELSIF event.type = SDL.quit THEN
+          PumpQuit
+        END
+      UNTIL SDL.PollEvent(event) = 0
+    END
+  END
+END WaitEvents;
+
+PROCEDURE PollEvent*(VAR event: Event): BOOLEAN;
+VAR hasEvent: BOOLEAN;
+BEGIN
+  IF events.len > 0 THEN
+    event := events.buf[events.first];
+    IF event.type = keyDown THEN DEC(keyPressed) END;
+    DEC(events.len); INC(events.first);
+    IF events.first = LEN(events.buf) THEN events.first := 0 END;
+    hasEvent := TRUE
+  ELSE hasEvent := FALSE
+  END;
+  RETURN hasEvent
+END PollEvent;
+
+PROCEDURE KeyPressed*(): BOOLEAN;
+BEGIN
+  WaitEvents(0);
+  RETURN keyPressed > 0
+END KeyPressed;
+
+PROCEDURE ReadKey*(): CHAR;
+VAR event: Event; done: BOOLEAN; ch: CHAR;
+BEGIN done := FALSE;
+  REPEAT
+    WaitEvents(-1);
+    WHILE PollEvent(event) DO
+      CASE event.type OF
+        keyDown: ch := CHR(event.key.sym); done := TRUE
+      | quit: ch := 0X; done := TRUE
+      ELSE
+      END
+    END
+  UNTIL done;
+  RETURN ch
+END ReadKey;
+
+PROCEDURE Pause*;
+BEGIN IF ReadKey() = 0X THEN END
+END Pause;
+
+PROCEDURE WindowShown*(): BOOLEAN;
+VAR flags: SET;
+BEGIN
+  flags := SDL.GetWindowFlags(window);
+  RETURN SDL.windowShown IN flags
+END WindowShown;
+
+PROCEDURE GetTicks*(): INTEGER;
+BEGIN
+  RETURN SDL.GetTicks()
+END GetTicks;
+
+PROCEDURE Flip*;
+VAR mx, my: INTEGER; (* Mouse bitmap X Y *)
+    blitMouse: BOOLEAN;
+    dt: INTEGER; (* Delta time *)
+
+  PROCEDURE PrepareMouse;
+  BEGIN
+    mx := mouseX - mouseFocusX;
+    my := mouseY - mouseFocusY;
+    (* Save image under mouse from buffer *)
+    Blit(screen, underMouse, mx, my,
+      underMouse.w, underMouse.h, 0, 0);
+    (* Blit mouse pointer onto buffer *)
+    Blit(mousePointer, screen, 0, 0,
+      mousePointer.w, mousePointer.h, mx, my)
+  END PrepareMouse;
+
+  PROCEDURE CleanMouse;
+  BEGIN
+    (* Restore image under mouse in buffer *)
+    Blit(underMouse, screen, 0, 0,
+      underMouse.w, underMouse.h, mx, my);
+    needRedrawMouse := FALSE
+  END CleanMouse;
+
+BEGIN
+  IF wantFPS # -1 THEN
+    IF lastFlip # -1 THEN
+      dt := 1000 DIV wantFPS - (GetTicks() - lastFlip);
+      IF (dt > 0) & (dt < 1000) THEN Delay(dt) END
+    END;
+    lastFlip := GetTicks()
+  END;
+  IF WindowShown() THEN
+    mx := 0; my := 0;
+    blitMouse := showMouse & MouseOnScreen();
+    IF blitMouse THEN PrepareMouse END;
+
+    (* Blit buffer on screen *)
+    SDL.SetRenderDrawColor(renderer, 0, 0, 0, 255);
+    SDL.RenderClear(renderer);
+    IF screenTexture # NIL THEN
+      SDL.DestroyTexture(screenTexture);
+      screenTexture := NIL
+    END;
+    screenTexture := SDL.CreateTextureFromSurface(renderer, screen.surface);
+    SDL.RenderCopyNil(renderer, screenTexture);
+    SDL.RenderPresent(renderer);
+
+    IF blitMouse THEN CleanMouse END
+  END
+END Flip;
+
+(* Sound *)
+
+(*
+PROCEDURE LoadSample*(filename: ARRAY OF CHAR): Sample;
+VAR spl: Sample;
+BEGIN
+  NEW(spl);
+  spl.chunk := Mix.LoadWavRW(SDL.RWFromFile(filename, 'rb'), 1);
+  IF spl.chunk = NIL THEN spl := NIL END;
+  RETURN spl
+END LoadSample;
+
+PROCEDURE PlayChannel*(spl: Sample; channel: INTEGER);
+BEGIN
+  IF spl # NIL THEN
+    IF Mix.PlayChannelTimed(channel, spl.chunk, 0, -1) = 0 THEN END
+  END
+END PlayChannel;
+
+PROCEDURE PlaySample*(spl: Sample);
+BEGIN
+  IF spl # NIL THEN PlayChannel(spl, -1) END
+END PlaySample;
+
+PROCEDURE StopSample*(spl: Sample);
+BEGIN
+  (*SDL.StopSample(spl)*)
+END StopSample;
+
+PROCEDURE LoadMusic*(filename: ARRAY OF CHAR): Music;
+VAR mus: Music;
+BEGIN
+  NEW(mus); mus.music := Mix.LoadMus(filename);
+  IF mus.music = NIL THEN mus := NIL END;
+  RETURN mus
+END LoadMusic;
+
+PROCEDURE PlayMusic*(mus: Music);
+BEGIN
+  IF (mus # NIL) & (mus.music # NIL) THEN
+    IF Mix.PlayMusic(mus.music, -1) = 0 THEN END
+  END
+END PlayMusic;
+
+PROCEDURE StopMusic*;
+BEGIN
+  Mix.HaltMusic
+END StopMusic;
+*)
+
+(* Net *)
+
+(*
+PROCEDURE OpenServer*(port: INTEGER): Socket;
+VAR addr: IPAddress; socket: Socket;
+BEGIN
+  IF Net.ResolveHost(addr, NIL, SHORT(port)) = 0
+  THEN socket := Net.TCPOpen(addr)
+  ELSE socket := NIL END;
+  RETURN socket
+END OpenServer;
+
+PROCEDURE CloseConn*(VAR socket: Socket);
+BEGIN
+  IF Net.TCPClose(socket) = 0 THEN socket := NIL END
+END CloseConn;
+
+PROCEDURE Accept*(server: Socket): Socket;
+BEGIN
+  RETURN Net.TCPAccept(server)
+END Accept;
+
+PROCEDURE ConnectTo*(ip: ARRAY OF CHAR; port: INTEGER): Socket;
+VAR addr: IPAddress; socket: Socket;
+BEGIN
+  IF Net.ResolveHost(addr, ip, SHORT(port)) = 0
+  THEN socket := Net.TCPOpen(addr)
+  ELSE socket := NIL END;
+  RETURN socket
+END ConnectTo;
+
+PROCEDURE SocketReady*(socket: Socket): BOOLEAN;
+BEGIN
+  RETURN (socket # NIL) & (socket.ready # 0)
+END SocketReady;
+
+PROCEDURE Send*(socket: Socket;
+  data: ARRAY OF CHAR; len: INTEGER): INTEGER;
+BEGIN
+  RETURN Net.TCPSend(socket, data, len)
+END Send;
+
+PROCEDURE SendStr*(socket: Socket; s: ARRAY OF CHAR): INTEGER;
+VAR len: INTEGER;
+BEGIN len := 0;
+  WHILE (len < LEN(s)) & (s[len] # 0X) DO INC(len) END;
+  RETURN Net.TCPSend(socket, s, len)
+END SendStr;
+
+PROCEDURE Recv*(socket: Socket;
+  VAR data: ARRAY OF CHAR; len: INTEGER): INTEGER;
+BEGIN
+  RETURN Net.TCPRecv(socket,
+    SYSTEM.VAL(SYSTEM.PTR, SYSTEM.ADR(data[0])), len)
+END Recv;
+
+PROCEDURE RecvStr*(socket: Socket; VAR s: ARRAY OF CHAR): INTEGER;
+VAR n: INTEGER;
+BEGIN
+  n := Recv(socket, s, LEN(s) - 1);
+  IF n >= 0 THEN
+    IF n >= LEN(s) THEN n := LEN(s) END;
+    s[n] := 0X
+  END;
+  RETURN n
+END RecvStr;
+*)
+
+(* Socket Set *)
+
+(*
+PROCEDURE AllocSocketSet*(maxSockets: INTEGER): SocketSet;
+BEGIN
+  RETURN Net.AllocSocketSet(maxSockets)
+END AllocSocketSet;
+
+PROCEDURE FreeSocketSet*(set: SocketSet);
+BEGIN
+  Net.FreeSocketSet(set)
+END FreeSocketSet;
+
+PROCEDURE AddSocket*(set: SocketSet; socket: Socket): INTEGER;
+BEGIN
+  RETURN Net.AddSocket(set, SYSTEM.VAL(Net.Socket, socket))
+END AddSocket;
+
+PROCEDURE DelSocket*(set: SocketSet; socket: Socket): INTEGER;
+BEGIN
+  RETURN Net.DelSocket(set, SYSTEM.VAL(Net.Socket, socket))
+END DelSocket;
+
+PROCEDURE CheckSockets*(set: SocketSet; timeout: INTEGER): BOOLEAN;
+BEGIN
+  RETURN Net.CheckSockets(set, timeout) # 0
+END CheckSockets;
+*)
+
+(* NetBuf *)
+
+(*
+PROCEDURE InitNetBuf*(buf: NetBuf; size: INTEGER);
+BEGIN
+  NEW(buf.s, size); buf.len := 0; buf.lastLen := 0
+END InitNetBuf;
+
+PROCEDURE NewNetBuf*(size: INTEGER): NetBuf;
+VAR buf: NetBuf;
+BEGIN
+  NEW(buf); InitNetBuf(buf, size); RETURN buf
+END NewNetBuf;
+
+PROCEDURE RecvBuf*(socket: Socket; buf: NetBuf);
+VAR n, len: INTEGER; s: PStr;
+BEGIN
+  len := LEN(buf.s^) - buf.len;
+  IF len = 0 THEN
+    NEW(s, LEN(buf.s^) * 2); COPY(buf.s^, s^);
+    buf.s := s
+  END;
+  n := Net.TCPRecv(socket,
+    SYSTEM.VAL(SYSTEM.PTR, SYSTEM.ADR(buf.s[buf.len])),
+    len);
+  buf.lastLen := buf.len;
+  IF n > 0 THEN
+    INC(buf.len, n);
+    IF buf.len > LEN(buf.s^) THEN buf.len := LEN(buf.s^) END
+  END
+END RecvBuf;
+
+PROCEDURE BufGet*(buf: NetBuf; sep: CHAR;
+  VAR s: ARRAY OF CHAR): BOOLEAN;
+VAR i: INTEGER; found: BOOLEAN;
+BEGIN i := SHORT(buf.lastLen);
+  WHILE (i < buf.len) & (buf.s[i] # sep) DO INC(i) END;
+  found := i < buf.len;
+  IF found THEN
+    S.Extract(buf.s^, 0, i, s);
+    S.Delete(buf.s^, 0, i + 1);
+    DEC(buf.len, i + 1); buf.lastLen := 0
+  END;
+  RETURN found
+END BufGet;
+*)
+
+(* Random *)
+
+PROCEDURE Time(): INTEGER;
+BEGIN
+  RETURN SHORT(Platform.Time())
+END Time;
+
+(* Set random seed value. Any values are allowed, although
+   values not in [1..2^31-2] will be mapped into this range. *)
+PROCEDURE PutSeed*(newSeed: INTEGER);
+BEGIN
+  newSeed := newSeed MOD randomModulo;
+  IF newSeed = 0 THEN randomSeed := 1
+  ELSE randomSeed := newSeed
+  END
+END PutSeed;
+
+PROCEDURE NextRND;
+CONST
+  a = 16807;
+  q = 127773; (* m div a *)
+  r = 2836;   (* m mod a *)
+VAR
+  lo, hi, test: INTEGER;
+BEGIN
+  hi := randomSeed DIV q;
+  lo := randomSeed MOD q;
+  test := a * lo - r * hi;
+  IF test > 0 THEN randomSeed := test
+  ELSE randomSeed := test + randomModulo
+  END
+END NextRND;
+
+(* Calculates a new number. range has to be in the intervall
+   [1..2^31-2]. Result is a number from 0, 1, ... , range-1. *)
+PROCEDURE Random*(range: INTEGER): INTEGER;
+BEGIN
+  NextRND;
+  RETURN randomSeed MOD range
+END Random;
+
+(* Calculates a number x with 0.0 <= x < 1.0. *)
+PROCEDURE Uniform*(): REAL;
+BEGIN
+  NextRND;
+  RETURN (randomSeed - 1) * (1 / (randomModulo - 1))
+END Uniform;
+
+PROCEDURE Randomize*;
+BEGIN
+  PutSeed(Time())
+END Randomize;
+
+(* Init *)
+
+PROCEDURE Init*(): Bitmap;
+VAR flags: SET; success: BOOLEAN; w, h, nw, nh: INTEGER;
+    s: ARRAY 2000 OF CHAR;
+BEGIN screen := NIL;
+  IF SDL.Init({SDL.initVideo}) = 0 THEN
+    flags := {};
+    IF wantFullscreen THEN
+      flags := flags + SDL.windowFullscreenDesktop;
+      IF (wantW <= 0) OR (wantH <= 0) THEN
+        GetDesktopResolution(wantW, wantH)
+      ELSIF wantSpread THEN
+        GetDesktopResolution(w, h);
+        IF wantSharpPixels THEN
+          nw := w DIV wantW; nh := h DIV wantH;
+          IF nw < nh THEN
+            wantW := w DIV nw; wantH := h DIV nw;
+          ELSE
+            wantW := w DIV nh; wantH := h DIV nh;
+          END
+        END;
+        IF w / h > wantW / wantH THEN wantW := w * wantH DIV h
+        ELSE wantH := h * wantW DIV w
+        END
+      END
+    ELSIF (wantW <= 0) OR (wantH <= 0) THEN wantW := 640; wantH := 400
+    END;
+    IF sizeStepX # 1 THEN wantW := wantW DIV sizeStepX * sizeStepX END;
+    IF sizeStepY # 1 THEN wantH := wantH DIV sizeStepY * sizeStepY END;
+    window := SDL.CreateWindow('',
+      SDL.windowPosUndefined, SDL.windowPosUndefined,
+      wantW, wantH, flags);
+    IF window # NIL THEN
+      IF wantSoftware THEN flags := {SDL.rendererSoftware}
+      ELSE flags := {SDL.rendererAccelerated}
+      END;
+      INCL(flags, SDL.rendererPresentVsync);
+      renderer := SDL.CreateRenderer(window, -1, flags);
+      IF wantSharpPixels THEN
+        SDL.SetHint(SDL.hintRenderScaleQuality, '0')
+      ELSE
+        SDL.SetHint(SDL.hintRenderScaleQuality, '1')
+      END;
+      SDL.RenderSetLogicalSize(renderer, wantW, wantH);
+      screen := CreateBitmap(wantW, wantH);
+      screenTexture := NIL;
+      UnsetRegion;
+      SDL.ShowCursor(0);
+      IF wantMouse THEN InitMouseData END;
+      flags := {SDL.imgInitPng, SDL.imgInitJpg};
+      IF flags - SDL.ImgInit(flags) # {} THEN
+        Out.String('Could not initialize PNG or JPG: ');
+        GetError(s); Out.String(s); Out.Ln
+      END;
+      (*
+      IF wantSound THEN
+        IF Mix.Init({}) = 0 THEN
+          IF Mix.OpenAudio(22050, Mix.defaultFormat,
+             4, 1024) = 0 THEN END
+        END
+      END;
+      IF wantNet THEN
+        IF Net.Init() = 0 THEN END
+      END
+      *)
+      Randomize;
+      keyPressed := 0;
+      lastFlip := -1
+    END
+  END;
+  RETURN screen
+END Init;
+
+PROCEDURE Close*;
+BEGIN
+  IF screenTexture # NIL THEN
+    SDL.DestroyTexture(screenTexture);
+    screenTexture := NIL
+  END;
+  (*
+  IF wantSound THEN Mix.Quit END;
+  IF wantNet THEN Net.Quit END;
+  *)
+  SDL.Quit
+END Close;
+
+BEGIN
+  wantW := 640; wantH := 400;
+  sizeStepX := 1; sizeStepY := 1;
+  wantFullscreen := TRUE; wantSpread := TRUE;
+  wantBuffer := FALSE; buffer := NIL; wantFPS := 60;
+  wantSharpPixels := TRUE;
+  mousePointer := NIL; lastBlitMouseOutside := FALSE;
+  mouseFocusX := 0; mouseFocusY := 0;
+  events.first := 0; events.last := -1; events.len := 0;
+  randomSeed := 1; keyPressed := 0
+END Graph.
+

+ 57 - 0
src/Semaphore.Mod

@@ -0,0 +1,57 @@
+MODULE Semaphore;
+(* Copyright 2020 Arthur Yefimov
+
+This file is part of Free Oberon.
+
+Free Oberon is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Free Oberon is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+*)
+IMPORT SYSTEM;
+
+CONST sizeOfSem = 320;
+
+TYPE
+  Sem* = RECORD
+    padding: ARRAY sizeOfSem OF CHAR
+  END;
+
+  TimeSpec* = RECORD
+  END;
+
+PROCEDURE -AAIncludeSemaphoreh0* '#include "Semaphore/Semaphore.h"';
+
+PROCEDURE -Close*(VAR s: Sem): INTEGER
+  "sem_close((sem_t *)s)";
+
+PROCEDURE -Destroy*(VAR s: Sem): INTEGER
+  "sem_destroy((sem_t *)s)";
+
+PROCEDURE -GetValue*(VAR s: Sem; VAR value: INTEGER): INTEGER
+  "sem_getvalue((sem_t *)s, (int *)value)";
+
+PROCEDURE -Init*(VAR s: Sem; a, b: INTEGER): INTEGER
+  "sem_init((sem_t *)s, (int)a, (unsigned int)b)";
+
+PROCEDURE -Post*(VAR s: Sem): INTEGER
+  "sem_post((sem_t *)s)";
+
+PROCEDURE -TryWait*(VAR s: Sem): INTEGER
+  "sem_trywait((sem_t *)s)";
+
+PROCEDURE -Wait*(VAR s: Sem): INTEGER
+  "sem_wait((sem_t *)s)";
+
+PROCEDURE -TimedWait*(VAR s: Sem; ms: INTEGER): INTEGER
+  "my_sem_timedwait((sem_t *)s, (int)ms)";
+
+END Semaphore.

+ 30 - 0
src/Semaphore.c0

@@ -0,0 +1,30 @@
+#ifndef Semaphore__h0
+#define Semaphore__h0
+
+#include <errno.h>
+#include <time.h>
+#include <semaphore.h>
+
+extern int my_sem_timedwait(sem_t *sem, int ms);
+int my_sem_timedwait(sem_t *sem, int ms) {
+  int res, a;
+  struct timespec ts;
+  if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
+    ts.tv_nsec += ms % 1000 * 1000;
+    if (ts.tv_nsec >= 1000000) {
+      a = ts.tv_nsec / 1000000;
+      ts.tv_nsec %= 1000000;
+      ts.tv_sec += 1 + ms / 1000;
+    } else {
+      ts.tv_sec += ms / 1000;
+    }
+    while ((res = sem_timedwait(sem, &ts)) == -1 && errno == EINTR) {
+      // Do nothing
+    }
+  } else {
+    res = -1;
+  }
+  return res;
+}
+
+#endif

+ 1 - 0
src/Semaphore/README.md

@@ -0,0 +1 @@
+# Helper routine for module Signals.

+ 43 - 0
src/Semaphore/Semaphore.c

@@ -0,0 +1,43 @@
+#include "Semaphore.h"
+
+#include <stdio.h>
+#include <time.h>
+
+static int NN = 0;
+
+int my_sem_timedwait(sem_t *sem, int ms) {
+  int res, a, s, diff;
+  struct timespec ts;
+
+  clock_t start, end;
+  double T;
+
+  if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
+    s = ms / 1000;
+    ms = ms % 1000;
+    ts.tv_nsec += ms * 1000000;
+    diff = ts.tv_nsec - 1000000000;
+    if (diff >= 0) {
+      ts.tv_nsec = diff;
+      ts.tv_sec += 1 + s;
+    } else {
+      ts.tv_sec += s;
+    }
+    
+    start = clock();
+
+    while ((res = sem_timedwait(sem, &ts)) == -1 && errno == EINTR) {
+      puts("EINTR\n");
+    }
+
+    end = clock();
+    T = ((double)(end - start)) / CLOCKS_PER_SEC;
+    //if (T > 1.0/50)
+    //printf("WAIT END %f   :: %d\n", T, NN); NN++;
+
+    if (res == -1 && errno == ETIMEDOUT) res = 1;
+  } else {
+    res = -1;
+  }
+  return res;
+}

+ 10 - 0
src/Semaphore/Semaphore.h

@@ -0,0 +1,10 @@
+#ifndef Semaphore__h0
+#define Semaphore__h0
+
+#include <errno.h>
+#include <time.h>
+#include <semaphore.h>
+
+extern int my_sem_timedwait(sem_t *sem, int ms);
+
+#endif

BIN
src/Semaphore/Semaphore.o


+ 72 - 0
src/Signals.Mod

@@ -0,0 +1,72 @@
+MODULE Signals;
+(* Copyright 2017-2020 Arthur Yefimov
+
+This file is part of Free Oberon.
+
+Free Oberon is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Free Oberon is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+*)
+IMPORT Semaphore, Out;
+
+TYPE
+  Signal* = RECORD
+    sem: Semaphore.Sem; (*sem must be aligned*)
+    ok: BOOLEAN
+  END;
+
+PROCEDURE Wait*(VAR s: Signal);
+BEGIN
+  IF ~s.ok OR (Semaphore.Wait(s.sem) # 0) THEN
+    Out.String("Error in Signals: Wait failed."); Out.Ln
+  END
+END Wait;
+
+(* Returns TRUE if signaled, FALSE if timed out. *)
+PROCEDURE TimedWait*(VAR s: Signal; n: INTEGER): BOOLEAN;
+VAR x: INTEGER;
+  r: BOOLEAN;
+BEGIN
+  IF ~s.ok THEN r := FALSE;
+    Out.String("Error in Signals: Could not TimedWait."); Out.Ln
+  ELSE
+    x := Semaphore.TimedWait(s.sem, n);
+    IF x = -1 THEN r := FALSE;
+      (*Out.String("Error in Signals: TimedWait failed."); Out.Ln*)(*!FIXME*)
+    ELSE r := x = 0
+    END
+  END;
+RETURN r END TimedWait;
+
+PROCEDURE Send*(VAR s: Signal);
+BEGIN
+  (*Out.String("SENDSIGNAL");Out.Ln;*)
+  IF ~s.ok OR (Semaphore.Post(s.sem) # 0) THEN
+    Out.String("Error in Signals: Send failed."); Out.Ln
+  END
+END Send;
+
+PROCEDURE Init*(VAR s: Signal);
+BEGIN
+  IF Semaphore.Init(s.sem, 0, 0) # 0 THEN
+    s.ok := FALSE;
+    Out.String("Error in Signals: Init failed."); Out.Ln
+  ELSE s.ok := TRUE
+  END
+END Init;
+
+PROCEDURE Close*(VAR s: Signal);
+BEGIN
+  (*!TODO*)
+END Close;
+
+END Signals.

+ 6 - 2
src/Term.Mod

@@ -1,5 +1,5 @@
 MODULE Term;
-(* Copyright 2017-2019 Arthur Yefimov
+(* Copyright 2017-2020 Arthur Yefimov
 
 This file is part of Free Oberon.
 
@@ -18,12 +18,16 @@ along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 *)
 IMPORT SYSTEM;
 
-PROCEDURE -AAIncludeTermh* '#include "term/term.h"';
+PROCEDURE -AAIncludeTermh* '#include "Term/Term.h"';
 
 PROCEDURE -StartProcess*
   (cmd: ARRAY OF CHAR): BOOLEAN
   "StartProcess(cmd)";
 
+PROCEDURE -StartProcessDir*
+  (cmd, dir: ARRAY OF CHAR): BOOLEAN
+  "StartProcessDir(cmd, dir)";
+
 PROCEDURE -ProcessFinished*(VAR err: INTEGER): BOOLEAN
   "ProcessFinished(err)";
 

+ 0 - 0
src/term/README.md → src/Term/README.md


+ 1 - 0
src/term/term.h → src/Term/Term.h

@@ -2,6 +2,7 @@
 #define TERM_H
 
 int StartProcess(char *process);
+int StartProcessDir(char *process, char *dir);
 int ProcessFinished();
 int WriteToProcess(char *buf, int len);
 int ReadFromProcess(char *buf, int *len, int limit);

BIN
src/Term/Term.o


+ 22 - 3
src/term/term_linux.c → src/Term/Term_linux.c

@@ -1,4 +1,4 @@
-/* Copyright 2017-2019 Arthur Yefimov
+/* Copyright 2017-2020 Arthur Yefimov
 
 This file is part of Free Oberon.
 
@@ -29,7 +29,7 @@ extern void exit(int);
 int p[2], q[2];
 pid_t pid;
 
-int StartProcess(char *process) {
+int StartProcessDir(char *process, char *dir) {
   int success = 0;
   if ((pipe(p) == -1) || (pipe(q) == -1)) {
     perror("StartProcess: Could not create pipes.");
@@ -46,20 +46,39 @@ int StartProcess(char *process) {
       }
       close(q[1]);
       close(p[0]);
+      if ((dir != NULL) && (dir != ".")) {
+        if (chdir(dir)) {
+          fprintf(stderr, "Could not change directory.\n");
+          exit(0);
+        }
+      }
+      fprintf(stderr, "[%s] [%s]\n", process, dir);
       if (execl(process, process, (char*)NULL)) {
-        puts("Could not run program.");
+        fprintf(stderr, "Could not run program.\n");
         exit(0);
       }
     } else { // Parent process
+
+      // Ignore SIGPIPE in case write() is used
+      // on a pipe that noone would read from
+      if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+        perror("signal(SIGPIPE, SIG_IGN)");
+      }
+
       close(q[0]);
       close(p[1]);
       fcntl(p[0], F_SETFL, O_NONBLOCK);
     }
     success = 1;
   }
+  puts("CCC3");
   return success;
 }
 
+int StartProcess(char *process) {
+  return StartProcessDir(process, NULL);
+}
+
 int ProcessFinished(int *err) {
   int status;
   pid_t result = waitpid(pid, &status, WNOHANG|WUNTRACED);

+ 6 - 2
src/term/term_win32.c → src/Term/Term_win32.c

@@ -1,4 +1,4 @@
-/* Copyright 2017-2019 Arthur Yefimov
+/* Copyright 2017-2020 Arthur Yefimov
 
 This file is part of Free Oberon.
 
@@ -116,7 +116,7 @@ BOOL MyCreatePipeEx(
 	return( TRUE );
 }
 
-int StartProcess(char *process) {
+int StartProcessDir(char *process, char *dir) {
   SECURITY_ATTRIBUTES saAttr;
 
 // Set the bInheritHandle flag so pipe handles are inherited.
@@ -216,6 +216,10 @@ int ProcessFinished(int *err) {
   return result;
 }
 
+int StartProcess(char *process) {
+  return StartProcessDir(process, NULL);
+}
+
 void WriteToProcess(char *buf, int len) {
   DWORD dwWritten;
 //  CHAR chBuf[BUFSIZE];

+ 46 - 40
src/Terminal.Mod

@@ -1,5 +1,5 @@
 MODULE Terminal;
-(* Copyright 2017-2019 Arthur Yefimov
+(* Copyright 2017-2020 Arthur Yefimov
 
 This file is part of Free Oberon.
 
@@ -45,7 +45,13 @@ VAR
   isFullscreen-: BOOLEAN;
 
 PROCEDURE Redraw*;
-BEGIN needRedraw := TRUE
+VAR x, y: INTEGER;
+BEGIN needRedraw := TRUE;
+  FOR y := 0 TO charsY - 1 DO
+    FOR x := 0 TO charsX - 1 DO
+      chars[y, x].updated := TRUE
+    END
+  END
 END Redraw;
 
 PROCEDURE ToggleFullscreen*;
@@ -91,30 +97,17 @@ END InvertColor;
 PROCEDURE DrawChar*(ch: CHAR; x, y, fg, bg: INTEGER);
 VAR color: INTEGER;
 BEGIN
-  G.RectFill(screen, x, y, x + charW - 1,
-    y + charH - 1, ExpandColor(bg));
+  G.RectFill(screen, x, y, x + charW - 1, y + charH - 1, ExpandColor(bg));
   IF ch # ' ' THEN
     G.DrawCharacter(screen, font, x, y, ch, ExpandColor(fg))
-  END
+  END;
+  G.AddRegion(x, y, charW, charH)
 END DrawChar;
 
-PROCEDURE DrawMouse;
-VAR x, y, bg, fg: INTEGER; ch: CHAR;
-BEGIN
-  IF (mouseX >= 0) & (mouseX < charsX) &
-     (mouseY >= 0) & (mouseY < charsY) THEN
-    bg := InvertColor(chars[mouseY, mouseX].bg);
-    fg := InvertColor(chars[mouseY, mouseX].fg);
-    ch := chars[mouseY, mouseX].ch
-  ELSE bg := 6; fg := 0; ch := ' ' END;
-  x := mouseX * charW;  y := mouseY * charH;
-  DrawChar(ch, x, y, fg, bg)
-END DrawMouse;
-
-(* Draws characters that have been changed, and the mouse.
+(* Draws characters that have been updated, and the mouse.
    Returns TRUE if something has been drawn. *)
 PROCEDURE Draw*(): BOOLEAN;
-VAR x, y, color: INTEGER;
+VAR x, y, fg, bg: INTEGER;
     drawn: BOOLEAN;
 BEGIN
   drawn := needRedraw;
@@ -125,26 +118,34 @@ BEGIN
       FOR x := 0 TO charsX - 1 DO
         IF chars[y, x].updated THEN
           chars[y, x].updated := FALSE;
-          DrawChar(chars[y, x].ch, x * charW, y * charH,
-            chars[y, x].fg, chars[y, x].bg)
+          fg := chars[y, x].fg;
+          bg := chars[y, x].bg;
+          IF (x = mouseX) & (y = mouseY) THEN
+            fg := InvertColor(fg);
+            bg := InvertColor(bg)
+          END;
+          DrawChar(chars[y, x].ch, x * charW, y * charH, fg, bg)
         END
       END
     END;
     (* Text Cursor *)
     IF cursorShown THEN
-      color := ExpandColor(chars[cursorY, cursorX].fg);
-      x := cursorX * charW;  y := cursorY * charH;
+      fg := chars[cursorY, cursorX].fg;
+      IF (mouseX = cursorX) & (mouseY = cursorY) THEN
+        fg := InvertColor(fg)
+      END;
+      fg := ExpandColor(fg);
+      x := cursorX * charW; y := cursorY * charH;
       IF insertCursor THEN
-        G.RectFill(screen, x, y, x + charW - 1, y + charH - 1, color) (*!FIXME*)
-      ELSE
-        INC(y, charH);
-        G.RectFill(screen, x, y - 2, x + charW - 1, y - 1, color)
+        G.RectFill(screen, x, y, x + charW - 1, y + charH - 1, fg); (*!FIXME*)
+        G.AddRegion(x, y, charW, charH) (*!FIXME return*)
+      ELSE INC(y, charH);
+        G.RectFill(screen, x, y - 2, x + charW - 1, y - 1, fg);
+        G.AddRegion(x, y - 2, charW, 2) (*!FIXME return*)
       END
-    END;
-    DrawMouse
+    END
   END;
-  RETURN drawn
-END Draw;
+RETURN drawn END Draw;
 
 PROCEDURE Act*;
 BEGIN
@@ -187,10 +188,14 @@ END GoToXY;
 
 PROCEDURE MouseXY*(x, y: INTEGER);
 BEGIN
-  needRedraw := TRUE;
-  chars[mouseY, mouseX].updated := TRUE;
-  mouseX := x; mouseY := y;
-  chars[mouseY, mouseX].updated := TRUE
+  IF x < 0 THEN x := 0 ELSIF x >= charsX THEN x := charsX - 1 END;
+  IF y < 0 THEN y := 0 ELSIF y >= charsY THEN y := charsY - 1 END;
+  IF (mouseX # x) OR (mouseY # y) THEN
+    needRedraw := TRUE;
+    chars[mouseY, mouseX].updated := TRUE;
+    mouseX := x; mouseY := y;
+    chars[mouseY, mouseX].updated := TRUE
+  END
 END MouseXY;
 
 PROCEDURE ResizeScreen;
@@ -320,7 +325,7 @@ END WriteString;
 PROCEDURE LoadMedia(): BOOLEAN;
 VAR success: BOOLEAN;
 BEGIN
-  font := G.LoadFont('data/images/font.bmp', charW, charH);
+  font := G.LoadFont('data/images/font.png', charW, charH);
   success := font # NIL;
   RETURN success
 END LoadMedia;
@@ -332,7 +337,8 @@ BEGIN
   options := {G.buffered, G.initMouse, G.spread};
   IF fullscreen THEN INCL(options, G.fullscreen) END;
   IF software THEN INCL(options, G.software) END;
-  G.Settings(640, 400, options);
+  G.Settings(640, 480, options);
+  G.Settings(1280, 720, options);
   G.SetSizeStep(charW, charH);
   screen := G.Init();
   IF screen # NIL THEN
@@ -346,8 +352,8 @@ BEGIN
       insertCursor := FALSE;
       cursorOn := FALSE;
       cursorShown := FALSE;
-      cursorX := 1;  cursorY := 2;
-      mouseX := 0;  mouseY := 0;
+      cursorX := 1; cursorY := 2;
+      mouseX := 0; mouseY := 0;
       ResizeScreen
     END
   END;

+ 254 - 0
src/al_icon/al_icon.c

@@ -0,0 +1,254 @@
+#include <allegro.h>
+/* XPM */
+static const char *allegico_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"48 48 191 2 ",
+"   c #61611B1B1616",
+".  c #63631B1B1616",
+"X  c #64641C1C1717",
+"o  c #66661C1C1717",
+"O  c #68681D1D1717",
+"+  c #69691D1D1818",
+"@  c #6B6B1E1E1818",
+"#  c #6C6C1E1E1919",
+"$  c #6E6E1F1F1919",
+"%  c #70701F1F1919",
+"&  c #71711F1F1A1A",
+"*  c #737320201A1A",
+"=  c #767621211B1B",
+"-  c #787821211B1B",
+";  c #7A7A22221B1B",
+":  c #7B7B22221C1C",
+">  c #7D7D23231C1C",
+",  c #7E7E23231D1D",
+"<  c #000016165A5A",
+"1  c #010116165A5A",
+"2  c #020217175B5B",
+"3  c #020218185D5D",
+"4  c #030319195D5D",
+"5  c #04041A1A5E5E",
+"6  c #05051B1B5F5F",
+"7  c #04041B1B6262",
+"8  c #06061D1D6363",
+"9  c #05051C1C6565",
+"0  c #06061D1D6565",
+"q  c #07071E1E6666",
+"w  c #08081F1F6666",
+"e  c #090920206767",
+"r  c #090921216969",
+"t  c #0A0A21216868",
+"y  c #0C0C23236B6B",
+"u  c #090922226D6D",
+"i  c #0A0A22226D6D",
+"p  c #0C0C24246F6F",
+"a  c #0D0D25256F6F",
+"s  c #0C0C24247171",
+"d  c #0E0E26267070",
+"f  c #0F0F27277070",
+"g  c #0E0E27277474",
+"h  c #0F0F29297878",
+"j  c #11112A2A7575",
+"k  c #11112A2A7777",
+"l  c #13132C2C7A7A",
+"z  c #11112C2C7D7D",
+"x  c #13132D2D7E7E",
+"c  c #15152F2F7F7F",
+"v  c #3B3B4A4A7575",
+"b  c #444453537C7C",
+"n  c #7E7E7C7C6F6F",
+"m  c #808024241D1D",
+"M  c #828224241D1D",
+"N  c #838324241E1E",
+"B  c #858525251E1E",
+"V  c #878725251E1E",
+"C  c #8A8A26261F1F",
+"Z  c #8B8B27272020",
+"A  c #8D8D27272020",
+"S  c #8F8F28282020",
+"D  c #909028282121",
+"F  c #949429292121",
+"G  c #95952A2A2222",
+"H  c #97972A2A2222",
+"J  c #98982A2A2323",
+"K  c #9A9A2B2B2323",
+"L  c #9D9D2C2C2424",
+"P  c #9F9F2C2C2424",
+"I  c #9B9B36362F2F",
+"U  c #A1A12D2D2424",
+"Y  c #A2A22D2D2525",
+"T  c #A4A42E2E2525",
+"R  c #A7A72F2F2626",
+"E  c #A9A92F2F2626",
+"W  c #AAAA2F2F2727",
+"Q  c #ACAC30302727",
+"!  c #AEAE30302727",
+"~  c #AFAF31312828",
+"^  c #B2B232322929",
+"/  c #B4B432322929",
+"(  c #B6B633332929",
+")  c #B7B733332A2A",
+"_  c #B9B934342A2A",
+"`  c #BBBB34342A2A",
+"'  c #BCBC34342B2B",
+"]  c #BEBE35352B2B",
+"[  c #BFBF35352C2C",
+"{  c #C1C136362C2C",
+"}  c #C4C437372D2D",
+"|  c #C6C637372D2D",
+" . c #C8C838382D2D",
+".. c #C9C938382E2E",
+"X. c #CBCB39392E2E",
+"o. c #CCCC39392F2F",
+"O. c #CECE3A3A2F2F",
+"+. c #CFCF3B3B3030",
+"@. c #CFCF3C3C3232",
+"#. c #D0D03E3E3333",
+"$. c #D0D03F3F3535",
+"%. c #B0B043433B3B",
+"&. c #D1D141413636",
+"*. c #D1D142423838",
+"=. c #D1D144443A3A",
+"-. c #D2D245453B3B",
+";. c #D2D247473D3D",
+":. c #D2D248483F3F",
+">. c #BFBF52524A4A",
+",. c #D3D34A4A4040",
+"<. c #D4D44D4D4343",
+"1. c #D4D44F4F4545",
+"2. c #D4D450504747",
+"3. c #D5D552524848",
+"4. c #C5C569696161",
+"5. c #DEDE77776F6F",
+"6. c #C9C97E7E7878",
+"7. c #DEDE78787171",
+"8. c #858583837575",
+"9. c #8D8D8B8B7D7D",
+"0. c #171731318080",
+"q. c #151530308484",
+"w. c #171733338787",
+"e. c #1A1A34348787",
+"r. c #171733338989",
+"t. c #1A1A35358989",
+"y. c #1B1B36368B8B",
+"u. c #1D1D38388C8C",
+"i. c #1E1E39398D8D",
+"p. c #1D1D39398E8E",
+"a. c #1E1E3B3B9494",
+"s. c #21213C3C9090",
+"d. c #22223E3E9696",
+"f. c #23233F3F9696",
+"g. c #21213E3E9999",
+"h. c #22223F3F9999",
+"j. c #242440409797",
+"k. c #232340409A9A",
+"l. c #262642429B9B",
+"z. c #242442429F9F",
+"x. c #27274545A0A0",
+"c. c #26264545A5A5",
+"v. c #2A2A4848A2A2",
+"b. c #2B2B4848A2A2",
+"n. c #2B2B4949A7A7",
+"m. c #2E2E4C4CA8A8",
+"M. c #2A2A4A4AACAC",
+"N. c #31315050AFAF",
+"B. c #2E2E4E4EB1B1",
+"V. c #35355454B3B3",
+"C. c #35355656BABA",
+"Z. c #3C3C5C5CBEBE",
+"A. c #3F3F5E5EBFBF",
+"S. c #3C3C5C5CC1C1",
+"D. c #3F3F6161C9C9",
+"F. c #43436262C3C3",
+"G. c #44446565CACA",
+"H. c #47476868CBCB",
+"J. c #49496969CCCC",
+"K. c #43436666D0D0",
+"L. c #47476969D1D1",
+"P. c #49496D6DDADA",
+"I. c #52527474DCDC",
+"U. c #58587A7AE0E0",
+"Y. c #58587B7BE6E6",
+"T. c #61618282E8E8",
+"R. c #61618484F0F0",
+"E. c #69698B8BF2F2",
+"W. c #6D6D8E8EF2F2",
+"Q. c #6D6D9191FEFE",
+"!. c #76769797FEFE",
+"~. c #919193939393",
+"^. c #929294949494",
+"/. c #939395959595",
+"(. c #949496969696",
+"). c #B7B78A8A8787",
+"_. c #B7B78C8C8989",
+"`. c #A4A4A2A29595",
+"'. c #B5B5B3B3A5A5",
+"]. c #CFCF92928D8D",
+"[. c #D4D4A3A39F9F",
+"{. c #D7D7B2B2AFAF",
+"}. c #DFDFC2C2BFBF",
+"|. c #9090ACACFEFE",
+" X c #CECECECECECE",
+".X c gray84",
+"XX c gray86",
+"oX c #F1F1C5C5C2C2",
+"OX c #F3F3F6F6FDFD",
+"+X c white",
+"@X c None",
+/* pixels */
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@XV V V V V V V V V V V ~.~.~.@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@XV V V V V V V V V V V V V ~.~.~.~.~.~.~.~.@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@XV V V V V V V V V V V V V V V ~.~.~.~.~.~.~.~.~.~.~.~.~.~.@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@XV V V V V V V V V V V V V V ~.~.~.~.~.~.^./././.^.~.~.v v < < < ~.@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X2.5.oXoXoX7.3.3.3.+X+X+X+X~.~.~.~.~.~.~.~./.(.b b a w 8 2 1 < < < < ~.@X@X",
+"@X@X@X@X@X@X@X@X@X@X;.;.,.5.5.5.2.2.3.3.+X+X+X+X~.~.~.~.v v 2 0 s x t.y.e.l p 8 2 < < < < ~.@X@X",
+"@X@X@X@X@X@X@X@X@X&.&.*.-.;.;.:.,.<.1.2.+X+X+X+X~.< < < < 1 9 s q.k.n.N.b.f.c y 6 1 < < < ~.@X@X",
+"@X@X@X@X@X@X@XX.+.@.#.#.$.&.&.=.-.;.;.:.+X+X+X+X~.< < < < 3 q z g.C.L.I.H.V.s.j q 2 < < < ~.@X@X",
+"@X@X@X@X@X@X| ....X.o.O.+.@.#.#.$.&.&.=.+X+X+X+X~.< < < 1 4 i w.c.K.R.!.T.F.l.0.t 4 1 < < ~.@X@X",
+"@X@X@X@X] ] [ { } | | ....X.o.O.+.#.#.#.+X+X+X+X~.< < < 1 4 i r.M.P.Q.|.W.J.v.0.t 4 1 < < ~.@X@X",
+"@X@X@X) ) _ ' ] ] [ [ { } | | ....X.o.O.+X+X+X+X~.< < < 1 4 i q.z.D.Y.E.U.A.j.c t 4 1 < < ~.@X@X",
+"@X@X@X^ / / ( ) ) _ ' ] ] [ [ { } |  ...+X+X+X+X~.< < < < 3 0 h a.B.S.G.Z.m.i.f 8 2 < < < ~.@X@X",
+"@X@X@XW Q ! ! / / / ( ) ) _ ' ] ] [ [ { +X+X+X+X~.< < < < 1 7 u z p.h.x.d.u.k e 5 1 < < < ~.@X@X",
+"@X@X@XR E E W W Q ! ~ ^ / / ( ) _ ` ] ] OX+X+X+X~.< < < < < 1 7 r g c c c d e 5 1 < < < < ~.@X@X",
+"@X@X@XU Y Y T R E W W W Q ! ~ / / / ( ) }.+X+X+X~.< < < < < 1 1 5 0 e e e 8 5 1 1 < < < < ~.@X@X",
+"@X@X@XL L P U U Y Y T R E W W W Q ! ^ / {.+X+X+X~.< < < < < < < 1 2 4 4 4 2 1 < < < < < < ~.@X@X",
+"@X@X@XG H J K L P P U U Y T T R E W W W [.+X+X+X~.< < < < < < < < < 1 1 1 < < < < < < < < ~.@X@X",
+"@X@X@XS D F G G H J K L P P U U Y T R R ].+X+X+X~.< < < < < < < < < < < < < < < < < < < < ~.@X@X",
+"@X@X@XZ Z Z S S D F G G H H K L P P U Y 6.+X+X+X~.< < < < < < < < < < < < < < < < < < < < ~.@X@X",
+"@X@X@XN V C C Z Z A S D D F G H H J K L 4.+X+X+X~.< < < < < < < < < < < < < < < < < < < < ~.@X@X",
+"@X@X@Xm m N N N V C C Z Z Z S D D F G H >.+X+X+X~.< < < < < < < < < < < < < < < < < < < < ~.@X@X",
+"@X@X@X; ; : , m m M N B V C C Z Z A S D %.+X+X+X~.< < < < < < < < < < < < < < < < < < < < ~.@X@X",
+"@X@X@X* = - - ; ; : , m m N N B V C C Z I +X+X+X~.< < < < < < < < < < < < < < < < < < < < ~.@X@X",
+"@X@X@X$ % & & * = - - ; ; : , m M N N B V +X+X+X~.< < < < < < < < < < < < < < < < < < < < ~.@X@X",
+"@X@X@XO + # $ $ % & & = = - - ; ; > , m m +X+X+X~.< < < < < < < < < < < < < < < < < < < < ~.@X@X",
+"@X@X@XX X X o O + # $ $ % & * = = - - ; : +X+X+X~.< < < < < < < < < < < < < < < < < < v ~.~.@X@X",
+"@X@X@X+X+X_.. X X X o O @ # $ $ & & * = - +X+X+X~.< < < < < < < < < < < < < < < < v ~.~.@X@X@X@X",
+"@X@X@X+X+X+X+X+X).  . X X X o O + # $ $ % +X+X+X~.< < < < < < < < < < < < < < v ~.~.@X@X@X@X@X@X",
+"@X@X@X+X+X+X+X+X+X+X+X).    . X X X O O @ +X+X+X~.< < < < < < < < < < < v ~.~.~.n @X@X@X@X@X@X@X",
+"@X@X@X@X@X+X+X+X+X+X+X+X+X+X+X).    . X X +X+X+X~.< < < < < < < < < v ~.~.n n n 8.@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X X+X+X+X+X+X+X+X+X+X+X).    +X+X+X~.< < < < < < < v ~.~.n n 8.8.8.9.9.@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X.X X X X+X+X+X+X+X+X+X+X+X+X+X+X+X~.< < < < v ~.~.~.n n 8.8.9.9.9.9.9.@X@X@X@X@X@X",
+"@X@X@X@X@X@X@XXX.X.X.X X X X X+X+X+X+X+X+X+X+X+X~.< < v ~.~.n n n 8.8.9.9.9.9.9.9.9.9.@X@X@X@X@X",
+"@X@X@X@X@X@X@X@XXXXXXX.X.X.X.X X X X+X+X+X+X+X+X~.v ~.~.n n 8.8.8.9.9.9.9.9.9.9.9.@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@XXXXXXXXXXX.X.X.X X X X+X+X+X~.~.n n 8.8.9.9.9.9.9.9.9.9.9.@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@XXXXXXXXXXXXX.X.X.X X X X`.n 8.8.9.9.9.9.9.9.9.9.9.@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@XXXXXXXXXXXXXXX.X.X.XXX`.9.9.9.9.9.9.9.9.9.@X@X@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@XXXXXXXXXXXXXXXXXXX'.9.9.9.9.9.9.@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@XXXXXXXXXXXXXXXXX`.9.9.9.@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@XXXXXXXXXXXXX'.9.@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@XXXXXXXXX@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@XXX@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X",
+"@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X@X"
+};
+#if defined ALLEGRO_WITH_XWINDOWS && defined ALLEGRO_USE_CONSTRUCTOR
+extern void *allegro_icon;
+CONSTRUCTOR_FUNCTION(static void _set_allegro_icon(void));
+static void _set_allegro_icon(void)
+{
+    allegro_icon = allegico_xpm;
+}
+#endif

BIN
src/al_icon/al_icon.o


+ 2 - 0
src/e

@@ -0,0 +1,2 @@
+#!/bin/bash
+vim -p FreeOberon.Mod Graph.Mod Signals.Mod Semaphore.Mod signals/signals.c Pthread.Mod Pthread.h0 Terminal.Mod EditorText.Mod Editor.Mod OV.Mod Allegro.Mod

Some files were not shown because too many files changed in this diff