Unix.Clipboard.Mod 9.0 KB

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