Unix.Clipboard.Mod 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. MODULE Clipboard; (** AUTHOR "G.F."; PUROSE "X11 clipboard interface"; *)
  2. IMPORT SYSTEM, Unix, Machine, X11, X11Api, Displays, XDisplay, Plugins, Log := KernelLog,
  3. Modules, Texts, TextUtilities, Strings, HostClipboard, Objects;
  4. CONST
  5. BufferSize = 2000H;
  6. TYPE
  7. Buffer = POINTER TO ARRAY BufferSize OF CHAR;
  8. Grabber = OBJECT
  9. CONST
  10. HSize = 256;
  11. VAR
  12. terminate: BOOLEAN;
  13. lastSelectionHead: ARRAY HSize OF CHAR;
  14. PROCEDURE &Init;
  15. BEGIN
  16. terminate := FALSE;
  17. lastSelectionHead := "";
  18. END Init;
  19. PROCEDURE SelectionIsNew(): BOOLEAN;
  20. VAR i: LONGINT; rc, lc: CHAR;
  21. BEGIN
  22. rc := recBuffer[0]; lc := lastSelectionHead[0]; i := 0;
  23. WHILE (rc = lc) & (rc # 0X) & (lc # 0X) & (i < HSize-1) DO
  24. INC( i ); rc := recBuffer[i]; lc := lastSelectionHead[i];
  25. END;
  26. RETURN rc # lc
  27. END SelectionIsNew;
  28. PROCEDURE SaveSelection;
  29. VAR i: LONGINT; c: CHAR;
  30. BEGIN
  31. i := 0;
  32. REPEAT c := recBuffer[i]; lastSelectionHead[i] := c; INC( i )
  33. UNTIL (c = 0X) OR (i >= HSize)
  34. END SaveSelection;
  35. BEGIN{ACTIVE}
  36. LOOP
  37. GetX11Selection;
  38. IF SelectionIsNew() THEN
  39. SaveSelection;
  40. UnixToA2;
  41. Texts.clipboard.AcquireWrite;
  42. Texts.clipboard.Delete( 0, Texts.clipboard.GetLength() );
  43. TextUtilities.StrToText( Texts.clipboard, 0, utf8Buffer^ );
  44. Texts.clipboard.ReleaseWrite;
  45. END;
  46. IF terminate THEN Objects.Terminate END;
  47. Objects.Sleep( 350 )
  48. END;
  49. END Grabber;
  50. VAR
  51. sendBuffer, recBuffer, utf8Buffer: Buffer;
  52. slen, rlen, ulen : LONGINT;
  53. received : BOOLEAN;
  54. grabber : Grabber;
  55. myProperty : X11.Atom;
  56. xdisp : X11.DisplayPtr;
  57. primary : X11.Window;
  58. secondary : X11.Window;
  59. PROCEDURE A2ToUnix; (* UTF-8 to XA_STRING *)
  60. VAR i, newlen, unicode: LONGINT;
  61. BEGIN
  62. i := 0; newlen := 0;
  63. WHILE i < slen DO
  64. unicode := Utf8ToUnicode( sendBuffer^, i );
  65. IF unicode >= 100H THEN unicode := 0B6H END;
  66. sendBuffer[newlen] := CHR( unicode );
  67. INC( newlen )
  68. END;
  69. slen := newlen
  70. END A2ToUnix;
  71. PROCEDURE UnixToA2;
  72. VAR i, d, tag: LONGINT; unicode, utflen: LONGINT;
  73. BEGIN
  74. i := 0; ulen := 0;
  75. WHILE (i < rlen) & (ulen < BufferSize - 5) DO
  76. unicode := ORD( recBuffer[i] ); INC( i );
  77. IF (unicode = ORD( '\' )) & (recBuffer[i] = 'u') THEN
  78. INC( i );
  79. unicode := ScanHexDigits( recBuffer^, i );
  80. END;
  81. IF unicode >= 800H THEN tag := 0E0H; utflen := 3
  82. ELSIF unicode >= 80H THEN tag := 0C0H; utflen := 2
  83. ELSE tag := 0; utflen := 1
  84. END;
  85. d := utflen - 1;
  86. WHILE d > 0 DO
  87. utf8Buffer[ulen + d] := CHR( 80H + unicode MOD 40H );
  88. unicode := unicode DIV 40H; DEC( d )
  89. END;
  90. utf8Buffer[ulen] := CHR( tag + unicode );
  91. INC( ulen, utflen )
  92. END;
  93. utf8Buffer[ulen] := 0X; INC( ulen )
  94. END UnixToA2;
  95. PROCEDURE ScanHexDigits( CONST buf: ARRAY OF CHAR; VAR pos: LONGINT ): LONGINT;
  96. VAR e, unicode: LONGINT; c: CHAR;
  97. BEGIN
  98. e := pos + 4; unicode := 0;
  99. REPEAT
  100. c := buf[pos]; INC( pos );
  101. IF (c >= '0') & (c <= '9' ) THEN unicode := unicode*10H + ORD( c ) - ORD( '0' )
  102. ELSIF (c >= 'a') & (c <= 'f') THEN unicode := unicode*10H + ORD( c ) - ORD( 'a' ) + 10
  103. ELSIF (c >= 'A') & (c <= 'F') THEN unicode := unicode*10H + ORD( c ) - ORD( 'A' ) + 10
  104. END;
  105. UNTIL pos = e;
  106. RETURN unicode
  107. END ScanHexDigits;
  108. PROCEDURE Utf8ToUnicode( CONST buf: ARRAY OF CHAR; VAR pos: LONGINT ): LONGINT;
  109. VAR unicode, tag, next, ch: LONGINT; ;
  110. BEGIN
  111. ch := ORD( buf[pos] ); INC( pos );
  112. IF ch < 128 THEN unicode := ch
  113. ELSE
  114. tag := ch DIV 10H; unicode := ch MOD 10H;
  115. IF tag = 0EH THEN next := pos + 2
  116. ELSE (* tag = 0CH *) next := pos + 1
  117. END;
  118. REPEAT
  119. unicode := unicode*40H + ORD( buf[pos] ) MOD 40H; INC( pos )
  120. UNTIL pos >= next;
  121. END;
  122. RETURN unicode
  123. END Utf8ToUnicode;
  124. PROCEDURE ClearSelection;
  125. BEGIN
  126. (* Texts.ClearLastSelection *)
  127. END ClearSelection;
  128. PROCEDURE ClipboardChanged( sender, data : ANY );
  129. BEGIN
  130. Texts.clipboard.AcquireRead;
  131. PutToClipboard( Texts.clipboard );
  132. Texts.clipboard.ReleaseRead;
  133. END ClipboardChanged;
  134. (** Copy text to X11 clipboard *)
  135. PROCEDURE PutToClipboard( text : Texts.Text );
  136. BEGIN
  137. ASSERT((text # NIL) & (text.HasReadLock()));
  138. TextUtilities.TextToStr( text, sendBuffer^ ); slen := Strings.Length( sendBuffer^ );
  139. A2ToUnix;
  140. Machine.Acquire( Machine.X11 );
  141. X11.SetSelectionOwner( xdisp, X11.XAPRIMARY, primary, X11.lastEventTime );
  142. Machine.Release( Machine.X11 );
  143. END PutToClipboard;
  144. PROCEDURE SendSelection( VAR event: X11Api.XSelectionRequestEvent );
  145. VAR ev: X11.SelectionEvent;
  146. BEGIN
  147. ev.typ := X11.SelectionNotify;
  148. ev.requestor := event.requestor;
  149. ev.selection := event.selection;
  150. ev.target := event.target;
  151. ev.time := event.time;
  152. IF (event.selection = X11.XAPRIMARY) & (event.target = X11.XASTRING) THEN
  153. ev.property := event.property;
  154. Machine.Acquire( Machine.X11 );
  155. X11.ChangeProperty( xdisp, ev.requestor, ev.property, ev.target, 8, X11.PropModeReplace,
  156. ADDRESSOF( sendBuffer[0] ), slen );
  157. Machine.Release( Machine.X11 );
  158. ELSE
  159. ev.property := X11.None
  160. END;
  161. Machine.Acquire( Machine.X11 );
  162. X11.SendEvent( xdisp, ev.requestor, X11.False, 0, ADDRESSOF(ev) );
  163. Machine.Release( Machine.X11 );
  164. END SendSelection;
  165. (** Copy text of X11 clipboard to text *)
  166. PROCEDURE GetFromClipboard( text : Texts.Text );
  167. BEGIN
  168. ASSERT((text # NIL) & (text.HasWriteLock()));
  169. GetX11Selection;
  170. UnixToA2;
  171. TextUtilities.StrToText( text, 0, utf8Buffer^ );
  172. END GetFromClipboard;
  173. PROCEDURE GetX11Selection;
  174. BEGIN{EXCLUSIVE}
  175. received := FALSE;
  176. Machine.Acquire( Machine.X11 );
  177. X11.ConvertSelection( xdisp, X11.XAPRIMARY, X11.XASTRING, myProperty, primary, X11.lastEventTime );
  178. Machine.Release( Machine.X11 );
  179. AWAIT( received );
  180. END GetX11Selection;
  181. PROCEDURE ReceiveSelection( VAR event: X11Api.XSelectionEvent );
  182. VAR type: X11.Atom; format, len, after: LONGINT; prop, adr: ADDRESS; ch: CHAR;
  183. BEGIN {EXCLUSIVE}
  184. rlen := 0; recBuffer[0] := 0X;
  185. IF (event.selection = X11.XAPRIMARY) & (event.property = myProperty) THEN
  186. Machine.Acquire( Machine.X11 );
  187. X11.GetWindowProperty( xdisp, event.requestor, event.property, 0, BufferSize, X11.False,
  188. event.target, type, format, len, after, prop );
  189. Machine.Release( Machine.X11 );
  190. adr := prop;
  191. IF len >= BufferSize THEN len := BufferSize - 2 END;
  192. WHILE len > 0 DO
  193. SYSTEM.GET( adr, ch ); INC( adr ); DEC( len );
  194. IF ch # 0X THEN recBuffer[rlen] := ch; INC( rlen ) END
  195. END;
  196. recBuffer[rlen] := 0X; INC( rlen );
  197. Machine.Acquire( Machine.X11 );
  198. X11.Free( prop ); X11.DeleteProperty( xdisp, event.requestor, event.property );
  199. Machine.Release( Machine.X11 );
  200. END;
  201. received := TRUE;
  202. END ReceiveSelection;
  203. PROCEDURE GetXDisplay;
  204. VAR p: Plugins.Plugin; disp: XDisplay.Display;
  205. BEGIN
  206. p := Displays.registry.Await("XDisplay");
  207. disp := p(XDisplay.Display);
  208. xdisp := disp.xdisp;
  209. primary := disp.primary;
  210. secondary := disp.secondary;
  211. END GetXDisplay;
  212. (* set Selection handlers to NIL *)
  213. PROCEDURE Cleanup;
  214. BEGIN
  215. grabber.terminate := TRUE;
  216. Objects.Sleep( 1000 );
  217. X11Api.SendSelection := NIL;
  218. X11Api.ReceiveSelection := NIL;
  219. X11Api.ClearSelection := NIL;
  220. Texts.clipboard.onTextChanged.Remove( ClipboardChanged );
  221. HostClipboard.SetHandlers( NIL, NIL );
  222. Log.Enter; Log.String( "X11 clipboard unregistered at host clipboard interface." ); Log.Exit;
  223. END Cleanup;
  224. PROCEDURE Install*;
  225. BEGIN
  226. IF Unix.Version = "Darwin" THEN
  227. Log.String( "Can't register the X11 clipboard in the Darwin port (ABI incompatiblity)" ); Log.Ln
  228. ELSE
  229. GetXDisplay;
  230. X11Api.SendSelection := SendSelection;
  231. X11Api.ReceiveSelection := ReceiveSelection;
  232. X11Api.ClearSelection := ClearSelection;
  233. Machine.Acquire( Machine.X11 );
  234. myProperty := X11.InternAtom( xdisp, ADDRESSOF("UNICODE"), X11.False );
  235. Machine.Release( Machine.X11 );
  236. NEW( sendBuffer ); NEW( recBuffer ); slen := 0; rlen := 0;
  237. NEW( utf8Buffer ); ulen := 0;
  238. (* register with AosText clipboard *)
  239. Texts.clipboard.onTextChanged.Add( ClipboardChanged );
  240. HostClipboard.SetHandlers( GetFromClipboard, PutToClipboard );
  241. NEW( grabber );
  242. Modules.InstallTermHandler( Cleanup );
  243. Log.Enter; Log.String("X11 clipboard registered at host clipboard interface."); Log.Exit;
  244. END
  245. END Install;
  246. BEGIN
  247. END Clipboard.
  248. Clipboard.Install ~
  249. SystemTools.Free Clipboard ~
  250. ---------------------------------------------
  251. Cut & paste between X11 applications and UnixAos.
  252. After performing 'Clipboard.Install ~' the X11 clipboard and the
  253. Aos clipboard get synchronized. Every new X11 selection modifies
  254. the Aos clipboard and the copy operation with the pie menu in
  255. Aos alters the primary X11 selection. The selection can then be
  256. inserted into an X11 application (e.g. the mail tool Thunderbird)
  257. by clicking the MM button.
  258. To stop the clipboard synchronization perform:
  259. 'SystemTools.Free Clipboard ~'.
  260. ---------------------------------------------