gpm.pp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by Peter Vreman
  4. GPM (>v1.17) mouse Interface for linux
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY;without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit gpm;
  12. {Note: Libgpm is *the* interface for Linux text-mode programs.
  13. Unfortunately it isn't suitable for anything else besides a blocky
  14. cursor on a text mode interface. The GPM protocol suffers from serious
  15. defficiencies and ideally, gpm is abolished as quickly as possible.
  16. With lack of a good alternative, GPM deserves good support. But
  17. please keep this in mind while coding.}
  18. {*****************************************************************************}
  19. interface
  20. {*****************************************************************************}
  21. uses
  22. baseUnix;
  23. {$ifdef use_external}
  24. {$linklib gpm}
  25. {$linklib c}
  26. {$endif}
  27. {$inline on}
  28. {$goto on}
  29. const
  30. _PATH_VARRUN = '/var/run/';
  31. _PATH_DEV = '/dev/';
  32. GPM_NODE_DIR = _PATH_VARRUN;
  33. GPM_NODE_DIR_MODE = 0775;
  34. GPM_NODE_PID = '/var/run/gpm.pid';
  35. GPM_NODE_DEV = '/dev/gpmctl';
  36. GPM_NODE_CTL = GPM_NODE_DEV;
  37. GPM_NODE_FIFO = '/dev/gpmdata';
  38. GPM_B_LEFT = 4;
  39. GPM_B_MIDDLE = 2;
  40. GPM_B_RIGHT = 1;
  41. type
  42. TGpmEtype = longint;
  43. TGpmMargin = longint;
  44. const
  45. GPM_MOVE = 1;
  46. GPM_DRAG = 2;
  47. GPM_DOWN = 4;
  48. GPM_UP = 8;
  49. GPM_SINGLE = 16;
  50. GPM_DOUBLE = 32;
  51. GPM_TRIPLE = 64;
  52. GPM_MFLAG = 128;
  53. GPM_HARD = 256;
  54. GPM_ENTER = 512;
  55. GPM_LEAVE = 1024;
  56. GPM_TOP = 1;
  57. GPM_BOT = 2;
  58. GPM_LFT = 4;
  59. GPM_RGT = 8;
  60. type
  61. {$PACKRECORDS c}
  62. Pgpm_event=^Tgpm_event;
  63. Tgpm_event=packed record
  64. buttons : byte;
  65. modifiers : byte;
  66. vc : word;
  67. dx : word;
  68. dy : word;
  69. x,y : word;
  70. EventType : TGpmEType;
  71. clicks : longint;
  72. margin : TGpmMargin;
  73. wdx,wdy : word;
  74. end;
  75. Pgpmevent=Pgpm_event;
  76. Tgpmevent=Tgpm_event;
  77. TGpmHandler=function(var event:TGpmEvent;clientdata:pointer):longint;cdecl;
  78. const
  79. GPM_MAGIC = $47706D4C;
  80. type
  81. Pgpm_connect = ^TGpm_connect;
  82. Tgpm_connect = packed record
  83. eventMask : word;
  84. defaultMask : word;
  85. minMod : word;
  86. maxMod : word;
  87. pid : longint;
  88. vc : longint;
  89. end;
  90. Pgpmconnect=Pgpm_connect;
  91. Tgpmconnect=Tgpm_connect;
  92. Pgpm_roi=^Tgpm_roi;
  93. Tgpm_roi=packed record
  94. xmin,xmax:integer;
  95. ymin,ymax:integer;
  96. minmod,maxmod:word;
  97. eventmask:word;
  98. owned:word;
  99. handler:Tgpmhandler;
  100. clientdata:pointer;
  101. prev,next:Pgpm_roi;
  102. end;
  103. Pgpmroi=Pgpm_roi;
  104. Tgpmroi=Tgpm_roi;
  105. {$ifdef external}
  106. var
  107. gpm_flag : longint;cvar;external;
  108. gpm_fd : longint;cvar;external;
  109. gpm_hflag : longint;cvar;external;
  110. gpm_morekeys : Longbool;cvar;external;
  111. gpm_zerobased : Longbool;cvar;external;
  112. gpm_visiblepointer : Longbool;cvar;external;
  113. gpm_mx : longint;cvar;external;
  114. gpm_my : longint;cvar;external;
  115. gpm_timeout : TTimeVal;cvar;external;
  116. _gpm_buf : array[0..0] of char;cvar;external;
  117. _gpm_arg : ^word;cvar;external;
  118. gpm_handler : TGpmHandler;cvar;external;
  119. gpm_data : pointer;cvar;external;
  120. gpm_roi_handler : TGpmHandler;cvar;external;
  121. gpm_roi_data : pointer;cvar;external;
  122. gpm_roi : PGpmRoi;cvar;external;
  123. gpm_current_roi : PGpmRoi;cvar;external;
  124. gpm_consolefd : longint;cvar;external;
  125. Gpm_HandleRoi : TGpmHandler;cvar;external;
  126. {$else}
  127. var gpm_roi:Pgpm_roi;
  128. gpm_handler,gpm_roi_handler:Tgpmhandler;
  129. gpm_current_roi:Pgpm_roi;
  130. gpm_roi_data:pointer;
  131. {$endif}
  132. function Gpm_StrictSingle(EventType : longint) : boolean;
  133. function Gpm_AnySingle(EventType : longint) : boolean;
  134. function Gpm_StrictDouble(EventType : longint) : boolean;
  135. function Gpm_AnyDouble(EventType : longint) : boolean;
  136. function Gpm_StrictTriple(EventType : longint) : boolean;
  137. function Gpm_AnyTriple(EventType : longint) : boolean;
  138. {$ifdef use_external}
  139. function Gpm_Open(var _para1:TGpmConnect; _para2:longint):longint;cdecl;external;
  140. function Gpm_Close:longint;cdecl;external;
  141. function Gpm_GetEvent(var _para1:TGpmEvent):longint;cdecl;external;
  142. {function Gpm_Getc(_para1:pFILE):longint;cdecl;external;
  143. function Gpm_Getchar : longint;}
  144. function Gpm_Repeat(millisec:longint):longint;cdecl;external;
  145. function Gpm_FitValuesM(var x,y:longint; margin:longint):longint;cdecl;external;
  146. function Gpm_FitValues(var x,y:longint):longint;cdecl;external;
  147. {function GPM_DRAWPOINTER(ePtr : longint) : longint;}
  148. function Gpm_PushRoi(x1:longint; y1:longint; X2:longint; Y2:longint; mask:longint; fun:TGpmHandler; xtradata:pointer):PGpmRoi;cdecl;external;
  149. function Gpm_PopRoi(which:PGpmRoi):PGpmRoi;cdecl;external;
  150. function Gpm_RaiseRoi(which:PGpmRoi; before:PGpmRoi):PGpmRoi;cdecl;external;
  151. function Gpm_LowerRoi(which:PGpmRoi; after:PGpmRoi):PGpmRoi;cdecl;external;
  152. {function Gpm_Wgetch:longint;cdecl;external;
  153. function Gpm_Getch:longint;}
  154. function Gpm_GetLibVersion(var where:longint):pchar;cdecl;external;
  155. function Gpm_GetServerVersion(var where:longint):pchar;cdecl;external;
  156. function Gpm_GetSnapshot(var ePtr:TGpmEvent):longint;cdecl;external;
  157. {$else}
  158. function gpm_open(var conn:Tgpm_connect;flag:longint):longint;
  159. function gpm_close:longint;
  160. function gpm_getevent(var event:Tgpm_event):longint;
  161. {function Gpm_Getc(_para1:pFILE):longint;cdecl;external;
  162. function Gpm_Getchar : longint;}
  163. function gpm_repeat(millisec:longint):longint;
  164. function gpm_fitvaluesM(var x,y:longint; margin:longint):longint;
  165. function gpm_fitvalues(var x,y:longint):longint;inline;
  166. function gpm_pushroi(x1:longint;y1:longint;x2:longint;y2:longint;
  167. mask:longint;fun:Tgpmhandler;xtradata:pointer):Pgpm_roi;
  168. function gpm_poproi(which:Pgpm_roi):Pgpm_roi;
  169. function gpm_raiseroi(which:Pgpm_roi;before:Pgpm_roi):Pgpm_roi;
  170. function gpm_lowerroi(which:Pgpm_roi;after:Pgpm_roi):Pgpm_roi;
  171. {Should be pointer because proc accepts nil.}
  172. function gpm_getsnapshot(eptr:Pgpmevent):longint;
  173. {Overload for compatibility.}
  174. function gpm_getsnapshot(var eptr:Tgpmevent):longint;inline;
  175. {$endif}
  176. {*****************************************************************************}
  177. implementation
  178. {*****************************************************************************}
  179. {$ifndef use_external}
  180. uses termio,sockets,strings,unix;
  181. type Pgpm_stst=^Tgpm_stst;
  182. Tgpm_stst=record
  183. info:Tgpmconnect;
  184. next:Pgpm_stst;
  185. end;
  186. Pmicetab=^Tmicetab;
  187. Tmicetab=record
  188. next:Pmicetab;
  189. device,protocol,options:Pchar;
  190. end;
  191. string63=string[63];
  192. Toptions=record
  193. autodetect:longint;
  194. mice_count:longint;
  195. repeater:longint;
  196. repeater_type:Pchar;
  197. run_status:longint;
  198. micelist:Pmicetab;
  199. progname,
  200. consolename:string63;
  201. end;
  202. var options:Toptions;
  203. gpm_stack:Pgpm_stst;
  204. gpm_mx,gpm_my:longint;
  205. gpm_saved_winch_hook,gpm_saved_suspend_hook:sigactionrec;
  206. const gpm_flag:boolean=false; {almost unuseful now -- where was it used for ? can
  207. we remove it now ? FIXME}
  208. gpm_tried:boolean=false;
  209. gpm_hflag:boolean=false;
  210. gpm_fd:longint=-1;
  211. gpm_consolefd:longint=-1;
  212. gpm_zerobased:longint=0;
  213. const GPM_DEVFS_CONSOLE='/dev/vc/0';
  214. GPM_OLD_CONSOLE='/dev/tty0';
  215. GPM_REQ_SNAPSHOT=0;
  216. GPM_REQ_BUTTONS=1;
  217. GPM_REQ_CONFIG=2;
  218. GPM_REQ_NOPASTE=3;
  219. {$endif}
  220. function Gpm_StrictSingle(EventType : longint) : boolean;
  221. begin
  222. Gpm_StrictSingle:=(EventType and GPM_SINGLE<>0) and not(EventType and GPM_MFLAG<>0);
  223. end;
  224. function Gpm_AnySingle(EventType : longint) : boolean;
  225. begin
  226. Gpm_AnySingle:=(EventType and GPM_SINGLE<>0);
  227. end;
  228. function Gpm_StrictDouble(EventType : longint) : boolean;
  229. begin
  230. Gpm_StrictDouble:=(EventType and GPM_DOUBLE<>0) and not(EventType and GPM_MFLAG<>0);
  231. end;
  232. function Gpm_AnyDouble(EventType : longint) : boolean;
  233. begin
  234. Gpm_AnyDouble:=(EventType and GPM_DOUBLE<>0);
  235. end;
  236. function Gpm_StrictTriple(EventType : longint) : boolean;
  237. begin
  238. Gpm_StrictTriple:=(EventType and GPM_TRIPLE<>0) and not(EventType and GPM_MFLAG<>0);
  239. end;
  240. function Gpm_AnyTriple(EventType : longint) : boolean;
  241. begin
  242. Gpm_AnyTriple:=(EventType and GPM_TRIPLE<>0);
  243. end;
  244. {$ifdef use_external}
  245. procedure Gpm_CheckVersion;
  246. var
  247. l : longint;
  248. begin
  249. Gpm_GetLibVersion(l);
  250. if l<11700 then
  251. begin
  252. writeln('You need at least gpm 1.17');
  253. halt(1);
  254. end;
  255. end;
  256. {$else}
  257. const checked_con:boolean=false;
  258. function putdata(where:longint;const what:Tgpmconnect):boolean;
  259. begin
  260. putdata:=true;
  261. if fpwrite(where,what,sizeof(Tgpmconnect))<>sizeof(Tgpmconnect) then
  262. begin
  263. { gpm_report(GPM_PR_ERR,GPM_MESS_WRITE_ERR,strerror(errno));}
  264. putdata:=false;
  265. end;
  266. end;
  267. function gpm_get_console:string63;
  268. var buf:stat;
  269. begin
  270. {First try the devfs device, because in the next time this will be
  271. the preferred one. If that fails, take the old console.}
  272. {Check for open new console.}
  273. if fpstat(GPM_DEVFS_CONSOLE,buf)=0 then
  274. gpm_get_console:=GPM_DEVFS_CONSOLE
  275. {Failed, try OLD console.}
  276. else if fpstat(GPM_OLD_CONSOLE,buf)=0 then
  277. gpm_get_console:=GPM_OLD_CONSOLE
  278. else
  279. gpm_get_console:='';
  280. end;
  281. procedure gpm_winch_hook(signum:longint;SigInfo: PSigInfo; SigContext: PSigContext);cdecl;
  282. var win:winsize;
  283. begin
  284. if (sigactionhandler(SIG_IGN)<>gpm_saved_winch_hook.sa_handler) and
  285. (sigactionhandler(SIG_DFL)<>gpm_saved_winch_hook.sa_handler) then
  286. gpm_saved_winch_hook.sa_handler(signum,nil,nil);
  287. if fpioctl(gpm_consolefd,TIOCGWINSZ,@win)=-1 then
  288. exit;
  289. if (win.ws_col=0) or (win.ws_row=0) then
  290. begin
  291. win.ws_col:=80;
  292. win.ws_row:=25;
  293. end;
  294. gpm_mx:=win.ws_col - gpm_zerobased;
  295. gpm_my:=win.ws_row - gpm_zerobased;
  296. end;
  297. procedure gpm_suspend_hook(signum:longint;SigInfo: PSigInfo; SigContext: PSigContext);cdecl;
  298. var conn:Tgpmconnect;
  299. old_sigset,new_sigset:Tsigset;
  300. sa:sigactionrec;
  301. success:boolean;
  302. begin
  303. fpsigemptyset(new_sigset);
  304. fpsigaddset(new_sigset,SIGTSTP);
  305. fpsigprocmask(SIG_BLOCK,new_sigset,old_sigset);
  306. {Open a completely transparent gpm connection.}
  307. conn.eventmask:=0;
  308. conn.defaultMask:=$ffff;
  309. conn.minmod:=$ffff;
  310. conn.maxmod:=0;
  311. {cannot do this under xterm, tough}
  312. success:=gpm_open(conn,0)>=0;
  313. {take the default action, whatever it is (probably a stop :)}
  314. fpsigprocmask(SIG_SETMASK,@old_sigset,nil);
  315. fpsigaction(SIGTSTP,@gpm_saved_suspend_hook,nil);
  316. fpkill(fpgetpid,SIGTSTP);
  317. { in bardo here }
  318. { Reincarnation. Prepare for another death early. }
  319. fpsigemptyset(sa.sa_mask);
  320. sa.sa_handler:=@gpm_suspend_hook;
  321. sa.sa_flags:=SA_NOMASK;
  322. fpsigaction(SIGTSTP,@sa,nil);
  323. { Pop the gpm stack by closing the useless connection }
  324. { but do it only when we know we opened one.. }
  325. if success then
  326. gpm_close;
  327. end;
  328. function gpm_open(var conn:Tgpmconnect;flag:longint):longint;
  329. var tty:string;
  330. flagstr:string[10];
  331. term:Pchar;
  332. i:cardinal;
  333. addr:Tunixsockaddr;
  334. win:Twinsize;
  335. n:Pgpm_stst;
  336. l:byte;
  337. p:byte; {there max 256 console ttys}
  338. buf:stat;
  339. sa:sigactionrec;
  340. label err;
  341. begin
  342. tty:='';
  343. options.consolename:='';
  344. { gpm_report(GPM_PR_DEBUG,"VC: %d",flag);}
  345. {....................................... First of all, check xterm}
  346. term:=fpgetenv('TERM');
  347. if (term<>nil) and (strcomp(term,'xterm')=0) then
  348. begin
  349. if gpm_tried then
  350. begin
  351. gpm_open:=gpm_fd; { no stack }
  352. exit;
  353. end;
  354. gpm_fd:=-2;
  355. {save old hilit tracking and enable mouse tracking}
  356. write(#27'[?1001s'#27'[?1000h');
  357. flush(output);
  358. gpm_flag:=true;
  359. gpm_open:=gpm_fd;
  360. exit;
  361. end;
  362. {....................................... No xterm, go on}
  363. { check whether we know what name the console is: what's with the lib??? }
  364. if not checked_con then
  365. begin
  366. options.consolename:=gpm_get_console;
  367. checked_con:=true;
  368. end;
  369. { So I chose to use the current tty, instead of /dev/console, which
  370. has permission problems. (I am fool, and my console is
  371. readable/writeable by everybody.
  372. However, making this piece of code work has been a real hassle.}
  373. if not gpm_flag and gpm_tried then
  374. begin
  375. gpm_open:=-1;
  376. exit;
  377. end;
  378. gpm_tried:=true; {do or die}
  379. new(n);
  380. n^.next:=gpm_stack;
  381. gpm_stack:=n;
  382. conn.pid:=fpgetpid; { fill obvious values }
  383. if n^.next<>nil then
  384. conn.vc:=n^.next^.info.vc {inherit}
  385. else
  386. begin
  387. conn.vc:=0; { default handler }
  388. if (flag>0) then
  389. begin { forced vc number }
  390. conn.vc:=flag;
  391. str(flag,flagstr);
  392. tty:=options.consolename+flagstr;
  393. end
  394. else
  395. begin {use your current vc}
  396. if isatty(0)<>0 then
  397. tty:=ttyname(0); { stdin }
  398. if (tty='') and (isatty(1)<>0) then
  399. tty:=ttyname(1); { stdout }
  400. if (tty='') and (isatty(2)<>0) then
  401. tty:=ttyname(2); { stderr }
  402. if (tty='') then
  403. begin
  404. { gpm_report(GPM_PR_ERR,"checking tty name failed");}
  405. goto err;
  406. end;
  407. conn.vc:=0;
  408. l:=length(tty);
  409. p:=1;
  410. while tty[l] in ['0'..'9'] do
  411. begin
  412. inc(conn.vc,p*(byte(tty[l])-byte('0')));
  413. p:=p*10;
  414. dec(l);
  415. end;
  416. end;
  417. if (gpm_consolefd=-1) then
  418. begin
  419. gpm_consolefd:=fpopen(tty,O_WRONLY);
  420. if gpm_consolefd<0 then
  421. begin
  422. { gpm_report(GPM_PR_ERR,GPM_MESS_DOUBLE_S,tty,strerror(errno));}
  423. goto err;
  424. end;
  425. end;
  426. end;
  427. n^.info:=conn;
  428. {....................................... Get screen dimensions }
  429. fpioctl(gpm_consolefd, TIOCGWINSZ, @win);
  430. if (win.ws_col or win.ws_row)=0 then
  431. begin
  432. {Hmmmm. The mad terminal didn't return it's size :/ }
  433. { fprintf(stderr, "libgpm: zero screen dimension, assuming 80x25.\n");}
  434. win.ws_col:=80;
  435. win.ws_row:=25;
  436. end;
  437. gpm_mx:=win.ws_col-gpm_zerobased;
  438. gpm_my:=win.ws_row-gpm_zerobased;
  439. {....................................... Connect to the control socket}
  440. if not gpm_flag then
  441. begin
  442. gpm_fd:=socket(AF_UNIX,SOCK_STREAM,0);
  443. if gpm_fd<0 then
  444. begin
  445. { gpm_report(GPM_PR_ERR,GPM_MESS_SOCKET,strerror(errno));}
  446. goto err;
  447. end;
  448. end;
  449. fillchar(addr,sizeof(addr),0);
  450. addr.family:=PF_UNIX;
  451. strcopy(addr.path, GPM_NODE_CTL);
  452. i:=sizeof(addr.family)+length(GPM_NODE_CTL);
  453. if fpconnect(gpm_fd,@addr,i)<0 then
  454. begin
  455. { gpm_report(GPM_PR_INFO,GPM_MESS_DOUBLE_S,GPM_NODE_CTL,strerror(errno));}
  456. {Well, try to open a chr device called /dev/gpmctl. This should
  457. be forward-compatible with a kernel server.}
  458. fpclose(gpm_fd); {the socket}
  459. gpm_fd:=fpopen(GPM_NODE_DEV,O_RDWR);
  460. if gpm_fd=-1 then
  461. begin
  462. { gpm_report(GPM_PR_ERR,GPM_MESS_DOUBLE_S,GPM_NODE_DEV
  463. ,strerror(errno));}
  464. goto err;
  465. end;
  466. if (fpfstat(gpm_fd,buf)=-1) or (buf.st_mode and STAT_IFMT<>STAT_IFCHR) then
  467. goto err;
  468. end;
  469. {....................................... Put your data}
  470. if putdata(gpm_fd,conn) then
  471. begin
  472. { itz Wed Dec 16 23:22:16 PST 1998 use sigaction, the old
  473. code caused a signal loop under XEmacs }
  474. fpsigemptyset(sa.sa_mask);
  475. { And the winch (window-resize) hook .. }
  476. sa.sa_handler:=@gpm_winch_hook;
  477. sa.sa_flags:=0;
  478. fpsigaction(SIGWINCH,@sa,@gpm_saved_winch_hook);
  479. if gpm_flag then
  480. begin
  481. { Install suspend hook }
  482. sa.sa_handler:=sigactionhandler(SIG_IGN);
  483. fpsigaction(SIGTSTP,@sa,@gpm_saved_suspend_hook);
  484. {if signal was originally ignored, job control is not supported}
  485. if gpm_saved_suspend_hook.sa_handler<>sigactionhandler(SIG_IGN) then
  486. begin
  487. sa.sa_flags:=SA_NOMASK;
  488. sa.sa_handler:=@gpm_suspend_hook;
  489. fpsigaction(SIGTSTP,@sa,nil);
  490. end;
  491. end;
  492. end;
  493. gpm_open:=gpm_fd;
  494. exit;
  495. {....................................... Error: free all memory}
  496. err:
  497. { gpm_report(GPM_PR_ERR,'Oh, oh, it''s an error! possibly I die! ');}
  498. repeat
  499. n:=gpm_stack^.next;
  500. dispose(gpm_stack);
  501. gpm_stack:=n;
  502. until gpm_stack=nil;
  503. if gpm_fd>=0 then
  504. fpclose(gpm_fd);
  505. gpm_flag:=false;
  506. gpm_open:=-1;
  507. end;
  508. function gpm_close:longint;
  509. var next:Pgpm_stst;
  510. begin
  511. gpm_tried:=false; { reset the error flag for next time }
  512. if gpm_fd=-2 then { xterm }
  513. begin
  514. write(#27'[?1000l'#27'[?1001r');
  515. flush(output);
  516. end
  517. else { linux }
  518. begin
  519. if not gpm_flag then
  520. gpm_close:=0
  521. else
  522. begin
  523. next:=gpm_stack^.next;
  524. dispose(gpm_stack);
  525. gpm_stack:=next;
  526. if next<>nil then
  527. putdata(gpm_fd,next^.info);
  528. gpm_flag:=false;
  529. end;
  530. end;
  531. if gpm_fd>=0 then
  532. fpclose(gpm_fd);
  533. gpm_fd:=-1;
  534. fpsigaction(SIGTSTP,@gpm_saved_suspend_hook,nil);
  535. fpsigaction(SIGWINCH,@gpm_saved_winch_hook,nil);
  536. fpclose(gpm_consolefd);
  537. gpm_consolefd:=-1;
  538. gpm_close:=0;
  539. end;
  540. function gpm_getevent(var event:Tgpm_event):longint;
  541. var count:longint;
  542. begin
  543. gpm_getevent:=0;
  544. if gpm_fd=-1 then
  545. exit;
  546. count:=fpread(gpm_fd,event,sizeof(Tgpm_event));
  547. if count<>sizeof(Tgpm_event) then
  548. begin
  549. {avoid to send the message if there is no data; sometimes it makes
  550. sense to poll the mouse descriptor any now an then using a
  551. non-blocking descriptor}
  552. { if (count<>-1) or (errno<>EAGAIN)
  553. gpm_report(GPM_PR_INFO,"Read too few bytes (%i) at %s:%d",
  554. count,__FILE__,__LINE__);}
  555. gpm_getevent:=-1;
  556. exit;
  557. end;
  558. dec(event.x,gpm_zerobased);
  559. dec(event.y,gpm_zerobased);
  560. gpm_getevent:=1;
  561. end;
  562. function gpm_repeat(millisec:longint):longint;
  563. var fd:longint;
  564. selset:Tfdset;
  565. begin
  566. fd:=0; {Default to stdin (xterm).}
  567. if gpm_fd>=0 then
  568. fd:=gpm_fd;
  569. fpFD_ZERO(selset);
  570. fpFD_SET(fd,selset);
  571. gpm_repeat:=fpselect(fd+1,@selset,nil,nil,millisec);
  572. end;
  573. function gpm_fitvaluesM(var x,y:longint;margin:longint):longint;
  574. begin
  575. gpm_fitvaluesM:=0;
  576. if margin=-1 then
  577. begin
  578. if x<gpm_zerobased then
  579. x:=gpm_zerobased
  580. else if x>gpm_mx then
  581. x:=gpm_mx;
  582. if y<gpm_zerobased then
  583. y:=gpm_zerobased
  584. else if y>gpm_my then
  585. y:=gpm_my;
  586. end
  587. else
  588. case margin of
  589. GPM_TOP:
  590. inc(y);
  591. GPM_BOT:
  592. dec(y);
  593. GPM_RGT:
  594. dec(x);
  595. GPM_LFT:
  596. inc(x);
  597. end;
  598. end;
  599. function gpm_fitvalues(var x,y:longint):longint;inline;
  600. begin
  601. gpm_fitvalues:=gpm_fitvaluesm(x,y,-1);
  602. end;
  603. function gpm_handle_roi(var eptr:Tgpm_event;clientdata:pointer):longint;cdecl;
  604. var backevent:Tgpm_event;
  605. roi:Pgpm_roi;
  606. begin
  607. roi:=gpm_current_roi;
  608. {If motion or press, look for the interested roi.
  609. Drag and release will be reported to the old roi.}
  610. if eptr.eventtype and (GPM_MOVE or GPM_DOWN)<>0 then
  611. begin
  612. roi:=gpm_roi;
  613. while roi<>nil do
  614. begin
  615. if not ((roi^.xmin>eptr.x) or (roi^.xmax<eptr.x)) and
  616. not ((roi^.ymin>eptr.y) or (roi^.ymax<eptr.y)) and
  617. not ((roi^.minmod and eptr.modifiers)<roi^.minmod) and
  618. not ((roi^.maxmod and eptr.modifiers)<eptr.modifiers) then
  619. break;
  620. roi:=roi^.next;
  621. end;
  622. end;
  623. {Now generate the leave/enter events}
  624. if roi<>gpm_current_roi then
  625. begin
  626. if (gpm_current_roi<>nil) and (gpm_current_roi^.eventmask and GPM_LEAVE<>0) then
  627. begin
  628. backevent.eventtype:=GPM_LEAVE;
  629. gpm_current_roi^.handler(backevent,gpm_current_roi^.clientdata);
  630. end;
  631. if (roi<>nil) and (roi^.eventmask and GPM_ENTER<>0) then
  632. begin
  633. backevent.eventtype:=GPM_ENTER;
  634. roi^.handler(backevent,roi^.clientdata);
  635. end;
  636. end;
  637. gpm_current_roi:=roi;
  638. {events not requested are discarded}
  639. if (roi<>nil) and (eptr.eventtype and ($0f or GPM_ENTER or GPM_LEAVE) and roi^.eventmask=0) then
  640. gpm_handle_roi:=0
  641. else
  642. begin
  643. backevent:=eptr; {copy it, so the main one is unchanged}
  644. if roi=nil then
  645. if gpm_roi_handler<>nil then
  646. gpm_handle_roi:=gpm_roi_handler(backevent,gpm_roi_data)
  647. else
  648. gpm_handle_roi:=0
  649. else
  650. begin
  651. {Ok, now report the event as it is, after modifying x and y}
  652. dec(backevent.x,roi^.xmin);
  653. dec(backevent.y,roi^.ymin);
  654. roi^.handler(backevent,roi^.clientdata);
  655. end;
  656. end;
  657. end;
  658. function gpm_pushroi(x1:longint;y1:longint;x2:longint;y2:longint;
  659. mask:longint;fun:Tgpmhandler;xtradata:pointer):Pgpm_roi;
  660. var n:Pgpm_roi;
  661. begin
  662. {create a roi and push it}
  663. new(n);
  664. {use the roi handler, if still null}
  665. if (gpm_roi<>nil) and (gpm_handler<>nil) then
  666. gpm_handler:=@gpm_handle_roi;
  667. n^.xmin:=x1; n^.xmax:=x2;
  668. n^.ymin:=y1; n^.ymax:=y2;
  669. n^.minmod:=0; n^.maxmod:=$ffff;
  670. n^.prev:=nil; n^.next:=nil;
  671. n^.eventmask:=mask;
  672. n^.owned:=0; { use dispose }
  673. n^.handler:=fun;
  674. if xtradata=nil then
  675. n^.clientdata:=n
  676. else
  677. n^.clientdata:=xtradata;
  678. gpm_pushroi:=gpm_raiseroi(n,nil);
  679. end;
  680. function gpm_useroi(n:Pgpm_roi):Pgpm_roi;
  681. begin
  682. { use a Roi by pushing it }
  683. n^.prev:=nil;
  684. n^.next:=nil;
  685. n^.owned:=1;
  686. { use the roi handler, if still nil }
  687. if (gpm_roi=nil) and (gpm_handler=nil) then
  688. gpm_handler:=@gpm_handle_roi;
  689. gpm_useroi:=gpm_raiseroi(n,nil);
  690. end;
  691. function gpm_poproi(which:Pgpmroi):Pgpmroi;
  692. begin
  693. {extract the Roi and remove it}
  694. if which^.prev<>nil then
  695. which^.prev^.next:=which^.next;
  696. if which^.next<>nil then
  697. which^.next^.prev:=which^.prev;
  698. if gpm_roi=which then
  699. gpm_roi:=which^.next;
  700. if which^.owned=0 then
  701. dispose(which);
  702. if gpm_current_roi=which then
  703. gpm_current_roi:=nil;
  704. gpm_poproi:=gpm_roi; {return the new top-of-stack}
  705. end;
  706. function gpm_raiseroi(which:Pgpmroi;before:Pgpmroi):Pgpmroi;
  707. begin
  708. {raise a Roi above another, or to top-of-stack}
  709. if gpm_roi=nil then
  710. begin
  711. gpm_roi:=which;
  712. gpm_raiseroi:=which;
  713. exit;
  714. end;
  715. if before=nil then
  716. before:=gpm_roi;
  717. if before=which then
  718. begin
  719. gpm_raiseroi:=gpm_roi;
  720. exit;
  721. end;
  722. if which^.prev<>nil then
  723. which^.prev^.next:=which^.next;
  724. if which^.next<>nil then
  725. which^.next^.prev:=which^.prev;
  726. if gpm_roi=which then
  727. gpm_roi:=which^.next;
  728. which^.prev:=before^.prev;
  729. before^.prev:=which;
  730. which^.next:=before;
  731. if which^.prev<>nil then
  732. which^.prev^.next:=which
  733. else
  734. gpm_roi:=which;
  735. gpm_raiseroi:=gpm_roi; { return the new top-of-stack }
  736. end;
  737. function gpm_lowerroi(which:Pgpmroi;after:Pgpmroi):Pgpmroi;
  738. begin
  739. {lower a Roi below another, or to bottom-of-stack}
  740. if after=nil then
  741. begin
  742. after:=gpm_roi;
  743. while after^.next<>nil do
  744. after:=after^.next;
  745. end;
  746. if after=which then
  747. begin
  748. gpm_lowerroi:=gpm_roi;
  749. exit;
  750. end;
  751. if which^.prev<>nil then
  752. which^.prev^.next:=which^.next;
  753. if which^.next<>nil then
  754. which^.next^.prev:=which^.prev;
  755. if gpm_roi=which then
  756. gpm_roi:=which^.next;
  757. which^.next:=after^.next;
  758. after^.next:=which;
  759. which^.prev:=after;
  760. if which^.next<>nil then
  761. which^.next^.prev:=which;
  762. gpm_lowerroi:=gpm_roi; {return the new top-of-stack}
  763. end;
  764. function gpm_getsnapshot(eptr:Pgpm_event):longint;
  765. var conn:Tgpm_connect;
  766. event:Tgpm_event;
  767. sillyset:Tfdset;
  768. i:longint;
  769. begin
  770. fillchar(conn,sizeof(conn),0);
  771. if eptr<>nil then
  772. conn.vc:=GPM_REQ_SNAPSHOT
  773. else
  774. begin
  775. conn.vc:=GPM_REQ_BUTTONS;
  776. eptr:=@event;
  777. end;
  778. if gpm_fd=-1 then
  779. begin
  780. gpm_getsnapshot:=-1;
  781. exit;
  782. end;
  783. fpFD_ZERO(sillyset);
  784. fpFD_SET(gpm_fd,sillyset);
  785. if fpselect(gpm_fd+1,@sillyset,nil,nil,0)=1 then
  786. gpm_getsnapshot:=0
  787. else
  788. begin
  789. fpwrite(gpm_fd,conn,sizeof(Tgpm_connect));
  790. i:=gpm_getevent(eptr^);
  791. if i<>1 then
  792. gpm_getsnapshot:=-1
  793. else
  794. begin
  795. gpm_getsnapshot:=eptr^.eventtype; { number of buttons }
  796. if eptr^.eventtype=0 then
  797. gpm_getsnapshot:=15;
  798. eptr^.eventtype:=0;
  799. end;
  800. end;
  801. end;
  802. function gpm_getsnapshot(var eptr:Tgpmevent):longint;inline;
  803. begin
  804. gpm_getsnapshot:=gpm_getsnapshot(@eptr);
  805. end;
  806. {$endif}
  807. end.