gr.ob07 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. MODULE gr; (* connect to libX11 *)
  2. IMPORT SYSTEM, unix, out;
  3. (*
  4. X11 documentation in:
  5. - http://tronche.com/gui/x/xlib/ an X11 reference
  6. - http://www.sbin.org/doc/Xlib an X11 tutorial (this domain has disappeared)
  7. *)
  8. CONST
  9. InputOutput = 1;
  10. StructureNotifyMask = 20000H; (* input event mask *)
  11. ExposureMask = 8000H; KeyPressMask = 1; KeyReleaseMask = 2;
  12. ButtonPressMask = 4; ButtonReleaseMask = 8; (* PointerNotionMask *)
  13. ZPixmap = 2;
  14. Expose = 12; (* X event type *) ConfigureNotify = 22; KeyPress = 2; ButtonPress = 4;
  15. EventTimeOut* = 80; (* 0, 0, 0, 0 *)
  16. EventResize* = 81; (* 0, w, h, 0 *)
  17. EventKeyPressed* = 82; (* isPrintable, keyCode (X11 scan code), state, keySym (ASCII) *)
  18. EventKeyReleased* = 83; (* 0, keyCode, state, 0 *)
  19. EventButtonPressed* = 84; (* button, x, y, state *)
  20. EventButtonReleased* = 85; (* button, x, y, state *)
  21. (* mouse button 1-5 = Left, Middle, Right, Scroll wheel up, Scroll wheel down *)
  22. bit64 = ORD(unix.BIT_DEPTH = 64);
  23. TYPE EventPars* = ARRAY 5 OF INTEGER;
  24. XEvent = RECORD
  25. val :ARRAY 192 OF BYTE (* union { ..., long pad[24]; } *)
  26. (* val :ARRAY 48 OF CARD32; *)
  27. END;
  28. VAR ScreenWidth*, ScreenHeight* :INTEGER;
  29. winWidth*, winHeight* :INTEGER; (* draw by writing to pixel buffer: *)
  30. base*, stride* :INTEGER; (* width, height, base ptr, stride in bytes, 32-bit RGB *)
  31. painting :BOOLEAN;
  32. libX11 :INTEGER; (* handle to dynamic library *)
  33. XOpenDisplay :PROCEDURE [linux] (name :INTEGER) :INTEGER;
  34. XCloseDisplay :PROCEDURE [linux] (display :INTEGER);
  35. XSynchronize :PROCEDURE [linux] (display, onoff :INTEGER) :INTEGER; (* return prev onoff *)
  36. XConnectionNumber :PROCEDURE [linux] (display :INTEGER) :INTEGER;
  37. XCreateWindow :PROCEDURE [linux] (display, parent_window, x, y, w, h, border_width, depth,
  38. class, visual, valuemask, attributes :INTEGER) :INTEGER; (* Window *)
  39. XDefaultScreen :PROCEDURE [linux] (display :INTEGER) :INTEGER;
  40. XDefaultGC :PROCEDURE [linux] (display, screenNr :INTEGER) :INTEGER; (* GC *)
  41. XDisplayWidth :PROCEDURE [linux] (display, screenNr :INTEGER) :INTEGER;
  42. XDisplayHeight :PROCEDURE [linux] (display, screenNr :INTEGER) :INTEGER;
  43. XDefaultVisual :PROCEDURE [linux] (display, screenNr :INTEGER) :INTEGER; (* visual *)
  44. XDefaultRootWindow :PROCEDURE [linux] (display :INTEGER) :INTEGER; (* Window *)
  45. XDefaultDepth :PROCEDURE [linux] (display, screenNr :INTEGER) :INTEGER;
  46. XSelectInput :PROCEDURE [linux] (display, window, event_mask :INTEGER);
  47. XMapWindow :PROCEDURE [linux] (display, window :INTEGER);
  48. XNextEvent :PROCEDURE [linux] (display, XEvent_p :INTEGER);
  49. XPending :PROCEDURE [linux] (display :INTEGER) :INTEGER;
  50. XLookupString :PROCEDURE [linux] (key_event, buffer_return, buflen, keysym_return, status_in_out :INTEGER) :INTEGER;
  51. XCreateImage :PROCEDURE [linux] (display, visual, depth, format, offset, data,
  52. width, height, bitmap_pad, bytes_per_line :INTEGER) :INTEGER; (* ptr to XImage *)
  53. XPutImage :PROCEDURE [linux] (display, window, gc, image, sx, sy, dx, dy, w, h :INTEGER);
  54. display, screen, window, gc, img :INTEGER;
  55. connectionNr :INTEGER; (* fd of X11 socket *)
  56. readX11 :unix.fd_set; (* used by select() timeout on X11 socket *)
  57. PROCEDURE getSymAdr (lib :INTEGER; name :ARRAY OF CHAR; adr :INTEGER);
  58. VAR sym :INTEGER;
  59. BEGIN
  60. sym := unix.dlsym (lib, SYSTEM.ADR(name[0]));
  61. IF sym = 0 THEN out.formatStr ("error: dlsym: %", name); out.nl END;
  62. ASSERT (sym # 0);
  63. SYSTEM.PUT (adr, sym)
  64. END getSymAdr;
  65. PROCEDURE init;
  66. BEGIN
  67. display := XOpenDisplay (0);
  68. IF display = 0 THEN out.str ("error: can not open X11 display."); out.nl; out.exit(1) END;
  69. (* ri := XSynchronize (display, 1); *)
  70. connectionNr := XConnectionNumber (display); ASSERT (connectionNr < unix.FD_SETSIZE);
  71. NEW (readX11); unix.FD_ZERO(readX11); unix.FD_SET (connectionNr, readX11);
  72. screen := XDefaultScreen (display); gc := XDefaultGC (display, screen);
  73. ScreenWidth := XDisplayWidth (display, screen); ScreenHeight := XDisplayHeight (display, screen);
  74. base := unix.malloc (ScreenWidth * ScreenHeight * 4);
  75. IF base = 0 THEN
  76. out.formatInt2 ("error: can not allocate screen buffer % x %", ScreenWidth, ScreenHeight); out.nl; out.exit(1);
  77. END;
  78. stride := ScreenWidth * 4;
  79. img := XCreateImage (display, XDefaultVisual (display, screen), XDefaultDepth (display, screen),
  80. ZPixmap, 0, base, ScreenWidth, ScreenHeight, 32, 0);
  81. END init;
  82. PROCEDURE finish*;
  83. VAR ri :INTEGER;
  84. BEGIN
  85. IF display # 0 THEN XCloseDisplay(display); display := 0 END;
  86. IF libX11 # 0 THEN ri := unix.dlclose (libX11); libX11 := 0 END;
  87. END finish;
  88. PROCEDURE createWindow* (w, h :INTEGER);
  89. VAR eventMask :INTEGER;
  90. BEGIN
  91. IF (w > ScreenWidth) OR (h > ScreenHeight) THEN
  92. out.str ("error: X11.createWindow: window too large"); out.exit(1);
  93. END;
  94. ASSERT ((w >= 0) & (h >= 0));
  95. window := XCreateWindow (display, XDefaultRootWindow (display), 0, 0, w, h, 0,
  96. XDefaultDepth (display, screen), InputOutput, XDefaultVisual (display, screen), 0, 0);
  97. winWidth := w; winHeight := h;
  98. eventMask := StructureNotifyMask + ExposureMask + KeyPressMask + ButtonPressMask;
  99. XSelectInput (display, window, eventMask);
  100. XMapWindow (display, window);
  101. END createWindow;
  102. PROCEDURE screenBegin*;
  103. (* intended to enable future cooperation with iOS / MacOS *)
  104. BEGIN
  105. ASSERT (~painting); painting := TRUE
  106. END screenBegin;
  107. PROCEDURE screenEnd*;
  108. BEGIN
  109. ASSERT (painting);
  110. XPutImage (display, window, gc, img, 0, 0, 0, 0, winWidth, winHeight);
  111. painting := FALSE;
  112. END screenEnd;
  113. PROCEDURE readInt (e :XEvent; i :INTEGER) :INTEGER;
  114. (* treat XEvent byte array as int array *)
  115. VAR n :INTEGER;
  116. BEGIN
  117. ASSERT (i >= 0);
  118. ASSERT (i < 48);
  119. i := i * 4;
  120. n := e.val[i+3]*1000000H + e.val[i+2]*10000H + e.val[i+1]*100H + e.val[i];
  121. RETURN n
  122. END readInt;
  123. PROCEDURE nextEvent* (msTimeOut :INTEGER; VAR ev :EventPars);
  124. VAR _type, n, ri :INTEGER;
  125. event :XEvent;
  126. x, y, w, h :INTEGER;
  127. timeout :unix.timespec;
  128. BEGIN
  129. (* struct XEvent (64-bit):
  130. any: 4 type 8 serial 4 send_event 8 display 8 window 8 window
  131. expose: 40 any 4 x, y, w, h, count
  132. xconfigure: 48 any 4 x, y, w, h
  133. xkey / xbutton / xmotion: 48 any 8 sub_window 8 time_ms 4 x, y, x_root, y_root 4 state 4 keycode/button
  134. *)
  135. (* struct XEvent (32-bit):
  136. any: 4 type 4 serial 4 send_event 4 display 4 window
  137. expose: 20 any 4 x, y, w, h, count
  138. xconfigure: 24 any 4 x, y, w, h
  139. xkey / xbutton / xmotion: 24 any 4 sub_window 4 time_ms 4 x, y, x_root, y_root 4 state 4 keycode/button
  140. *)
  141. _type := 0;
  142. WHILE _type = 0 DO
  143. IF (msTimeOut > 0) & (XPending(display) = 0) THEN
  144. timeout.tv_sec := msTimeOut DIV 1000; timeout.tv_usec := (msTimeOut MOD 1000) * 1000;
  145. ri := unix.select (connectionNr + 1, readX11, NIL, NIL, timeout); ASSERT (ri # -1);
  146. IF ri = 0 THEN _type := EventTimeOut; ev[1] := 0; ev[2] := 0; ev[3] := 0; ev[4] := 0 END;
  147. END;
  148. IF _type = 0 THEN
  149. XNextEvent (display, SYSTEM.ADR(event));
  150. CASE readInt (event, 0) OF
  151. Expose :
  152. x := readInt (event, 5 + 5 * bit64); y := readInt (event, 6 + 5 * bit64);
  153. w := readInt (event, 7 + 5 * bit64); h := readInt (event, 8 + 5 * bit64);
  154. XPutImage (display, window, gc, img, x, y, x, y, w, h);
  155. | ConfigureNotify :
  156. w := readInt (event, 8 + 6 * bit64); h := readInt (event, 9 + 6 * bit64);
  157. IF (w # winWidth) & (h # winHeight) THEN
  158. ASSERT ((w >= 0) & (h >= 0));
  159. IF w > ScreenWidth THEN w := ScreenWidth END;
  160. IF h > ScreenHeight THEN h := ScreenHeight END;
  161. winWidth := w; winHeight := h;
  162. ev[0] := EventResize; ev[1] := 0; ev[2] := w; ev[3] := h; ev[4] := 0;
  163. END;
  164. | KeyPress :
  165. _type := EventKeyPressed;
  166. x := XLookupString (SYSTEM.ADR(event), 0, 0, SYSTEM.ADR(n), 0); (* KeySym *)
  167. IF (n = 8) OR (n = 10) OR (n >= 32) & (n <= 126) THEN ev[1] := 1 ELSE ev[1] := 0; n := 0 END; (* isprint *)
  168. ev[2] := readInt (event, 13 + 8 * bit64); (* keycode *)
  169. ev[3] := readInt (event, 12 + 8 * bit64); (* state *)
  170. ev[4] := n; (* KeySym *)
  171. | ButtonPress :
  172. _type := EventButtonPressed;
  173. ev[1] := readInt (event, 13 + 8 * bit64); (* button *)
  174. ev[2] := readInt (event, 8 + 8 * bit64); (* x *)
  175. ev[3] := readInt (event, 9 + 8 * bit64); (* y *)
  176. ev[4] := readInt (event, 12 + 8 * bit64); (* state *)
  177. ELSE
  178. END
  179. END
  180. END;
  181. ev[0] := _type
  182. END nextEvent;
  183. PROCEDURE clear* (color :INTEGER); (* fill window area with color *)
  184. VAR p, i, j :INTEGER;
  185. BEGIN
  186. FOR j := 0 TO winHeight-1 DO
  187. p := base + j*stride;
  188. FOR i := 0 TO winWidth-1 DO SYSTEM.PUT32 (p, color); INC (p, 4) END
  189. END
  190. END clear;
  191. (*
  192. PROCEDURE blitError (stride, x, y, w, h :INTEGER);
  193. BEGIN
  194. o.formatInt ("error: screen.blit (src, %)", stride);
  195. o.formatInt2 (", %, %", x, y);
  196. o.formatInt2 (", %, %) out of bounds", w, h); o.nl;
  197. ASSERT (FALSE)
  198. END blitError;
  199. PROCEDURE blit* (src, srcStride, x, y, w, h :INTEGER);
  200. VAR dstStride, p :INTEGER;
  201. BEGIN
  202. IF (x < 0) OR (y < 0) THEN blitError (srcStride, x, y, w, h) END;
  203. IF (w <= 0) OR (h <= 0) THEN blitError (srcStride, x, y, w, h) END;
  204. IF (x + w > ScreenWidth) OR (y + h > ScreenHeight) THEN blitError (srcStride, x, y, w, h) END;
  205. dstStride := ScreenWidth - w;
  206. p := ScreenBase + y * ScreenWidth + x * 4;
  207. REPEAT
  208. SYSTEM.COPY (src, p, w);
  209. INC (src, srcStride); INC (p, dstStride); DEC (h)
  210. UNTIL h = 0
  211. END blit;
  212. *)
  213. (*
  214. PROCEDURE setPixel* (x, y, color :INTEGER);
  215. VAR p :INTEGER;
  216. BEGIN
  217. ASSERT ((x >= 0) & (x < ScreenWidth) & (y >= 0) & (y < ScreenHeight));
  218. screenBegin; p := base + (y*ScreenWidth + x)*4; SYSTEM.PUT32 (p, color); p := p + 4 screenEnd
  219. END setPixel;
  220. *)
  221. (*
  222. PROCEDURE loop; (* example main loop *)
  223. VAR e :EventPars;
  224. stop :BOOLEAN;
  225. BEGIN
  226. createWindow (200, 200);
  227. stop := FALSE;
  228. REPEAT
  229. nextEvent (0, e);
  230. IF e[0] = EventKeyPressed THEN stop := TRUE END;
  231. UNTIL stop;
  232. XCloseDisplay (display);
  233. END loop;
  234. *)
  235. BEGIN
  236. libX11 := unix.dlopen (SYSTEM.SADR("libX11.so.6"), unix.RTLD_LAZY); ASSERT (libX11 # 0);
  237. getSymAdr (libX11, "XOpenDisplay", SYSTEM.ADR(XOpenDisplay));
  238. getSymAdr (libX11, "XCloseDisplay", SYSTEM.ADR(XCloseDisplay));
  239. getSymAdr (libX11, "XSynchronize", SYSTEM.ADR(XSynchronize));
  240. getSymAdr (libX11, "XConnectionNumber", SYSTEM.ADR(XConnectionNumber));
  241. getSymAdr (libX11, "XCreateWindow", SYSTEM.ADR(XCreateWindow));
  242. getSymAdr (libX11, "XDefaultScreen", SYSTEM.ADR(XDefaultScreen));
  243. getSymAdr (libX11, "XDefaultGC", SYSTEM.ADR(XDefaultGC));
  244. getSymAdr (libX11, "XDisplayWidth", SYSTEM.ADR(XDisplayWidth));
  245. getSymAdr (libX11, "XDisplayHeight", SYSTEM.ADR(XDisplayHeight));
  246. getSymAdr (libX11, "XDefaultVisual", SYSTEM.ADR(XDefaultVisual));
  247. getSymAdr (libX11, "XDefaultRootWindow", SYSTEM.ADR(XDefaultRootWindow));
  248. getSymAdr (libX11, "XDefaultDepth", SYSTEM.ADR(XDefaultDepth));
  249. getSymAdr (libX11, "XSelectInput", SYSTEM.ADR(XSelectInput));
  250. getSymAdr (libX11, "XMapWindow", SYSTEM.ADR(XMapWindow));
  251. getSymAdr (libX11, "XNextEvent", SYSTEM.ADR(XNextEvent));
  252. getSymAdr (libX11, "XPending", SYSTEM.ADR(XPending));
  253. getSymAdr (libX11, "XLookupString", SYSTEM.ADR(XLookupString));
  254. getSymAdr (libX11, "XCreateImage", SYSTEM.ADR(XCreateImage));
  255. getSymAdr (libX11, "XPutImage", SYSTEM.ADR(XPutImage));
  256. init;
  257. END gr.