|
|
1.1 root 1: /* $Id: mouse.c,v 1.22 2004/11/18 01:29:04 rswindell Exp $ */
2:
3: /****************************************************************************
4: * @format.tab-size 4 (Plain Text/Source Code File Header) *
5: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
6: * *
7: * Copyright 2004 Rob Swindell - http://www.synchro.net/copyright.html *
8: * *
9: * This library is free software; you can redistribute it and/or *
10: * modify it under the terms of the GNU Lesser General Public License *
11: * as published by the Free Software Foundation; either version 2 *
12: * of the License, or (at your option) any later version. *
13: * See the GNU Lesser General Public License for more details: lgpl.txt or *
14: * http://www.fsf.org/copyleft/lesser.html *
15: * *
16: * Anonymous FTP access to the most recent released source is available at *
17: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
18: * *
19: * Anonymous CVS access to the development source and modification history *
20: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
21: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
22: * (just hit return, no password is necessary) *
23: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
24: * *
25: * For Synchronet coding style and modification guidelines, see *
26: * http://www.synchro.net/source.html *
27: * *
28: * You are encouraged to submit any modifications (preferably in Unix diff *
29: * format) via e-mail to [email protected] *
30: * *
31: * Note: If this box doesn't appear square, then you need to fix your tabs. *
32: ****************************************************************************/
33:
34: #include <stdlib.h>
35:
36: #include <genwrap.h>
37: #include <semwrap.h>
38: #include <threadwrap.h>
39: #include <link_list.h>
40:
41: #include "mouse.h"
42:
43: #define MSEC_CLOCK() (msclock()*MSCLOCKS_PER_SEC/1000)
44:
45: enum {
46: MOUSE_NOSTATE
47: ,MOUSE_SINGLEPRESSED
48: ,MOUSE_CLICKED
49: ,MOUSE_DOUBLEPRESSED
50: ,MOUSE_DOUBLECLICKED
51: ,MOUSE_TRIPLEPRESSED
52: ,MOUSE_TRIPLECLICKED
53: ,MOUSE_QUADPRESSED
54: ,MOUSE_QUADCLICKED
55: ,MOUSE_DRAGSTARTED
56: };
57:
58: pthread_mutex_t in_mutex;
59: pthread_mutex_t out_mutex;
60: sem_t in_sem;
61:
62: struct in_mouse_event {
63: int event;
64: int x;
65: int y;
66: clock_t ts;
67: void *nextevent;
68: };
69:
70: struct out_mouse_event {
71: int event;
72: int bstate;
73: int kbsm; /* Known button state mask */
74: int startx;
75: int starty;
76: int endx;
77: int endy;
78: void *nextevent;
79: };
80:
81: struct mouse_state {
82: int buttonstate; /* Current state of all buttons - bitmap */
83: int knownbuttonstatemask; /* Mask of buttons that have done something since
84: * We started watching... the rest are actually in
85: * an unknown state */
86: int button_state[3]; /* Expanded state of each button */
87: int button_x[3]; /* Start X/Y position of the current state */
88: int button_y[3];
89: clock_t timeout[3]; /* Button event timeouts (timespecs ie: time of expiry) */
90: int curx; /* Current X position */
91: int cury; /* Current Y position */
92: int events; /* Currently enabled events */
93: int click_timeout; /* Timeout between press and release events for a click (ms) */
94: int multi_timeout; /* Timeout after a click for detection of multi clicks (ms) */
95: int click_drift; /* Allowed "drift" during a click event */
96: link_list_t input;
97: link_list_t output;
98: };
99:
100: struct mouse_state state;
101: int mouse_events=0;
102: static mouse_initialized=0;
103:
104: void init_mouse(void)
105: {
106: memset(&state,0,sizeof(state));
107: state.click_timeout=0;
108: state.multi_timeout=300;
109: listInit(&state.input,0);
110: listInit(&state.output,0);
111: pthread_mutex_init(&in_mutex,NULL);
112: pthread_mutex_init(&out_mutex,NULL);
113: sem_init(&in_sem,0,0);
114: mouse_initialized=1;
115: }
116:
117: int ciomouse_setevents(int events)
118: {
119: mouse_events=events;
120: return mouse_events;
121: }
122:
123: int ciomouse_addevents(int events)
124: {
125: mouse_events |= events;
126: return mouse_events;
127: }
128:
129: int ciomouse_delevents(int events)
130: {
131: mouse_events &= ~events;
132: return mouse_events;
133: }
134:
135: int ciomouse_addevent(int event)
136: {
137: mouse_events |= (1<<event);
138: return mouse_events;
139: }
140:
141: int ciomouse_delevent(int event)
142: {
143: mouse_events &= ~(1<<event);
144: return mouse_events;
145: }
146:
147: void ciomouse_gotevent(int event, int x, int y)
148: {
149: struct in_mouse_event *ime;
150:
151: while(!mouse_initialized)
152: SLEEP(1);
153: ime=(struct in_mouse_event *)malloc(sizeof(struct in_mouse_event));
154: ime->ts=MSEC_CLOCK();
155: ime->event=event;
156: ime->x=x;
157: ime->y=y;
158: ime->nextevent=NULL;
159:
160: pthread_mutex_lock(&in_mutex);
161:
162: listPushNode(&state.input,ime);
163:
164: pthread_mutex_unlock(&in_mutex);
165: sem_post(&in_sem);
166: }
167:
168: void add_outevent(int event, int x, int y)
169: {
170: struct out_mouse_event *ome;
171: int but=0;
172:
173: if(!(mouse_events & 1<<event))
174: return;
175: ome=(struct out_mouse_event *)malloc(sizeof(struct out_mouse_event));
176:
177: but=CIOLIB_BUTTON_NUMBER(event);
178: ome->event=event;
179: ome->bstate=state.buttonstate;
180: ome->kbsm=state.knownbuttonstatemask;
181: ome->startx=but?state.button_x[but-1]:state.curx;
182: ome->starty=but?state.button_y[but-1]:state.cury;
183: ome->endx=x;
184: ome->endy=y;
185: ome->nextevent=(struct out_mouse_event *)NULL;
186:
187: pthread_mutex_lock(&out_mutex);
188:
189: listPushNode(&state.output,ome);
190:
191: pthread_mutex_unlock(&out_mutex);
192: }
193:
194: int more_multies(int button, int clicks)
195: {
196: switch(clicks) {
197: case 1:
198: if(mouse_events & (1<<CIOLIB_BUTTON_DBL_CLICK(button)))
199: return(1);
200: case 2:
201: if(mouse_events & (1<<CIOLIB_BUTTON_TRPL_CLICK(button)))
202: return(1);
203: case 3:
204: if(mouse_events & (1<<CIOLIB_BUTTON_QUAD_CLICK(button)))
205: return(1);
206: }
207: return(0);
208: }
209:
210: void ciolib_mouse_thread(void *data)
211: {
212: int timedout;
213: int timeout_button=0;
214: int but;
215: int delay;
216: clock_t ttime=0;
217:
218: init_mouse();
219: while(1) {
220: timedout=0;
221: if(timeout_button) {
222: delay=state.timeout[timeout_button-1]-MSEC_CLOCK();
223: if(delay<=0) {
224: timedout=1;
225: }
226: else {
227: timedout=sem_trywait_block(&in_sem,delay);
228: }
229: }
230: else {
231: sem_wait(&in_sem);
232: }
233: if(timedout) {
234: state.timeout[timeout_button-1]=0;
235: switch(state.button_state[timeout_button-1]) {
236: case MOUSE_SINGLEPRESSED:
237: /* Press event */
238: add_outevent(CIOLIB_BUTTON_PRESS(timeout_button),state.button_x[timeout_button-1],state.button_y[timeout_button-1]);
239: break;
240: case MOUSE_CLICKED:
241: /* Click Event */
242: add_outevent(CIOLIB_BUTTON_CLICK(timeout_button),state.button_x[timeout_button-1],state.button_y[timeout_button-1]);
243: break;
244: case MOUSE_DOUBLEPRESSED:
245: /* Click event, then press event */
246: add_outevent(CIOLIB_BUTTON_CLICK(timeout_button),state.button_x[timeout_button-1],state.button_y[timeout_button-1]);
247: add_outevent(CIOLIB_BUTTON_PRESS(timeout_button),state.button_x[timeout_button-1],state.button_y[timeout_button-1]);
248: break;
249: case MOUSE_DOUBLECLICKED:
250: /* Double-click event */
251: add_outevent(CIOLIB_BUTTON_DBL_CLICK(timeout_button),state.button_x[timeout_button-1],state.button_y[timeout_button-1]);
252: break;
253: case MOUSE_TRIPLEPRESSED:
254: /* Double-click event, then press event */
255: add_outevent(CIOLIB_BUTTON_DBL_CLICK(timeout_button),state.button_x[timeout_button-1],state.button_y[timeout_button-1]);
256: add_outevent(CIOLIB_BUTTON_PRESS(timeout_button),state.button_x[timeout_button-1],state.button_y[timeout_button-1]);
257: break;
258: case MOUSE_TRIPLECLICKED:
259: /* Triple-click event */
260: add_outevent(CIOLIB_BUTTON_TRPL_CLICK(timeout_button),state.button_x[timeout_button-1],state.button_y[timeout_button-1]);
261: break;
262: case MOUSE_QUADPRESSED:
263: /* Triple-click evetn then press event */
264: add_outevent(CIOLIB_BUTTON_TRPL_CLICK(timeout_button),state.button_x[timeout_button-1],state.button_y[timeout_button-1]);
265: add_outevent(CIOLIB_BUTTON_PRESS(timeout_button),state.button_x[timeout_button-1],state.button_y[timeout_button-1]);
266: break;
267: case MOUSE_QUADCLICKED:
268: add_outevent(CIOLIB_BUTTON_QUAD_CLICK(timeout_button),state.button_x[timeout_button-1],state.button_y[timeout_button-1]);
269: /* Quad click event (This doesn't need a timeout does it? */
270: break;
271: }
272: state.button_state[timeout_button-1]=MOUSE_NOSTATE;
273: }
274: else {
275: struct in_mouse_event *in;
276:
277: pthread_mutex_lock(&in_mutex);
278: in=listShiftNode(&state.input);
279: pthread_mutex_unlock(&in_mutex);
280: if(in==NULL)
281: continue;
282: but=CIOLIB_BUTTON_NUMBER(in->event);
283: switch(CIOLIB_BUTTON_BASE(in->event)) {
284: case CIOLIB_MOUSE_MOVE:
285: if(in->x==state.curx
286: && in->y==state.cury)
287: break;
288: add_outevent(CIOLIB_MOUSE_MOVE,in->x,in->y);
289: for(but=1;but<=3;but++) {
290: switch(state.button_state[but-1]) {
291: case MOUSE_NOSTATE:
292: if(state.buttonstate & CIOLIB_BUTTON(but)) {
293: add_outevent(CIOLIB_BUTTON_DRAG_START(but),state.button_x[but-1],state.button_y[but-1]);
294: add_outevent(CIOLIB_BUTTON_DRAG_MOVE(but),in->x,in->y);
295: state.button_state[but-1]=MOUSE_DRAGSTARTED;
296: }
297: break;
298: case MOUSE_SINGLEPRESSED:
299: add_outevent(CIOLIB_BUTTON_DRAG_START(but),state.button_x[but-1],state.button_y[but-1]);
300: add_outevent(CIOLIB_BUTTON_DRAG_MOVE(but),in->x,in->y);
301: state.button_state[but-1]=MOUSE_DRAGSTARTED;
302: break;
303: case MOUSE_CLICKED:
304: add_outevent(CIOLIB_BUTTON_CLICK(but),state.button_x[but-1],state.button_y[but-1]);
305: state.button_state[but-1]=MOUSE_NOSTATE;
306: break;
307: case MOUSE_DOUBLEPRESSED:
308: add_outevent(CIOLIB_BUTTON_CLICK(but),state.button_x[but-1],state.button_y[but-1]);
309: add_outevent(CIOLIB_BUTTON_DRAG_START(but),state.button_x[but-1],state.button_y[but-1]);
310: add_outevent(CIOLIB_BUTTON_DRAG_MOVE(but),in->x,in->y);
311: state.button_state[but-1]=MOUSE_DRAGSTARTED;
312: break;
313: case MOUSE_DOUBLECLICKED:
314: add_outevent(CIOLIB_BUTTON_DBL_CLICK(but),state.button_x[but-1],state.button_y[but-1]);
315: state.button_state[but-1]=MOUSE_NOSTATE;
316: break;
317: case MOUSE_TRIPLEPRESSED:
318: add_outevent(CIOLIB_BUTTON_DBL_CLICK(but),state.button_x[but-1],state.button_y[but-1]);
319: add_outevent(CIOLIB_BUTTON_DRAG_START(but),state.button_x[but-1],state.button_y[but-1]);
320: add_outevent(CIOLIB_BUTTON_DRAG_MOVE(but),in->x,in->y);
321: state.button_state[but-1]=MOUSE_DRAGSTARTED;
322: break;
323: case MOUSE_TRIPLECLICKED:
324: add_outevent(CIOLIB_BUTTON_TRPL_CLICK(but),state.button_x[but-1],state.button_y[but-1]);
325: state.button_state[but-1]=MOUSE_NOSTATE;
326: break;
327: case MOUSE_QUADPRESSED:
328: add_outevent(CIOLIB_BUTTON_TRPL_CLICK(but),state.button_x[but-1],state.button_y[but-1]);
329: add_outevent(CIOLIB_BUTTON_DRAG_START(but),state.button_x[but-1],state.button_y[but-1]);
330: add_outevent(CIOLIB_BUTTON_DRAG_MOVE(but),in->x,in->y);
331: state.button_state[but-1]=MOUSE_DRAGSTARTED;
332: break;
333: case MOUSE_DRAGSTARTED:
334: add_outevent(CIOLIB_BUTTON_DRAG_MOVE(but),in->x,in->y);
335: break;
336: }
337: }
338: break;
339: case CIOLIB_BUTTON_1_PRESS:
340: state.buttonstate|=1<<(but-1);
341: state.knownbuttonstatemask|=1<<(but-1);
342: switch(state.button_state[but-1]) {
343: case MOUSE_NOSTATE:
344: state.button_state[but-1]=MOUSE_SINGLEPRESSED;
345: state.button_x[but-1]=in->x;
346: state.button_y[but-1]=in->y;
347: state.timeout[but-1]=MSEC_CLOCK()+state.click_timeout;
348: if(state.timeout[but-1]==0)
349: state.timeout[but-1]=1;
350: if(state.click_timeout==0)
351: state.timeout[but-1]=0;
352: break;
353: case MOUSE_CLICKED:
354: state.button_state[but-1]=MOUSE_DOUBLEPRESSED;
355: state.timeout[but-1]=MSEC_CLOCK()+state.click_timeout;
356: if(state.timeout[but-1]==0)
357: state.timeout[but-1]=1;
358: if(state.click_timeout==0)
359: state.timeout[but-1]=0;
360: break;
361: case MOUSE_DOUBLECLICKED:
362: state.button_state[but-1]=MOUSE_TRIPLEPRESSED;
363: state.timeout[but-1]=MSEC_CLOCK()+state.click_timeout;
364: if(state.timeout[but-1]==0)
365: state.timeout[but-1]=1;
366: if(state.click_timeout==0)
367: state.timeout[but-1]=0;
368: break;
369: case MOUSE_TRIPLECLICKED:
370: state.button_state[but-1]=MOUSE_QUADPRESSED;
371: state.timeout[but-1]=MSEC_CLOCK()+state.click_timeout;
372: if(state.timeout[but-1]==0)
373: state.timeout[but-1]=1;
374: if(state.click_timeout==0)
375: state.timeout[but-1]=0;
376: break;
377: }
378: break;
379: case CIOLIB_BUTTON_1_RELEASE:
380: state.buttonstate&= ~(1<<(but-1));
381: state.knownbuttonstatemask|=1<<(but-1);
382: switch(state.button_state[but-1]) {
383: case MOUSE_NOSTATE:
384: state.button_x[but-1]=in->x;
385: state.button_y[but-1]=in->y;
386: add_outevent(CIOLIB_BUTTON_RELEASE(but),state.button_x[but-1],state.button_y[but-1]);
387: break;
388: case MOUSE_SINGLEPRESSED:
389: state.button_state[but-1]=MOUSE_CLICKED;
390: state.timeout[but-1]=more_multies(but,1)?MSEC_CLOCK()+state.multi_timeout:MSEC_CLOCK();
391: if(state.timeout[but-1]==0)
392: state.timeout[but-1]=1;
393: break;
394: case MOUSE_DOUBLEPRESSED:
395: state.button_state[but-1]=MOUSE_DOUBLECLICKED;
396: state.timeout[but-1]=more_multies(but,2)?MSEC_CLOCK()+state.multi_timeout:MSEC_CLOCK();
397: if(state.timeout[but-1]==0)
398: state.timeout[but-1]=1;
399: break;
400: case MOUSE_TRIPLEPRESSED:
401: state.button_state[but-1]=MOUSE_TRIPLECLICKED;
402: state.timeout[but-1]=more_multies(but,3)?MSEC_CLOCK()+state.multi_timeout:MSEC_CLOCK();
403: if(state.timeout[but-1]==0)
404: state.timeout[but-1]=1;
405: break;
406: case MOUSE_QUADPRESSED:
407: state.button_state[but-1]=MOUSE_NOSTATE;
408: add_outevent(CIOLIB_BUTTON_QUAD_CLICK(but),state.button_x[but-1],state.button_y[but-1]);
409: state.timeout[but-1]=0;
410: if(state.timeout[but-1]==0)
411: state.timeout[but-1]=1;
412: break;
413: case MOUSE_DRAGSTARTED:
414: add_outevent(CIOLIB_BUTTON_DRAG_END(but),in->x,in->y);
415: state.button_state[but-1]=0;
416: }
417: }
418: state.curx=in->x;
419: state.cury=in->y;
420:
421: free(in);
422: }
423:
424: timeout_button=0;
425: for(but=1;but<=3;but++) {
426: if(state.button_state[but-1]!=MOUSE_NOSTATE
427: && state.button_state[but-1]!=MOUSE_DRAGSTARTED
428: && state.timeout[but-1]!=0
429: && (timeout_button==0 || state.timeout[but-1]<ttime)) {
430: ttime=state.timeout[but-1];
431: timeout_button=but;
432: }
433: }
434: }
435: }
436:
437: int mouse_pending(void)
438: {
439: while(!mouse_initialized)
440: SLEEP(1);
441: return(listCountNodes(&state.output));
442: }
443:
444: int ciolib_getmouse(struct mouse_event *mevent)
445: {
446: int retval=0;
447:
448: while(!mouse_initialized)
449: SLEEP(1);
450: if(listCountNodes(&state.output)) {
451: struct out_mouse_event *out;
452: pthread_mutex_lock(&out_mutex);
453: out=listShiftNode(&state.output);
454: pthread_mutex_unlock(&out_mutex);
455: if(out==NULL)
456: return(-1);
457: mevent->event=out->event;
458: mevent->bstate=out->bstate;
459: mevent->kbsm=out->kbsm;
460: mevent->startx=out->startx;
461: mevent->starty=out->starty;
462: mevent->endx=out->endx;
463: mevent->endy=out->endy;
464: free(out);
465: }
466: else {
467: memset(mevent,0,sizeof(struct mouse_event));
468: retval=-1;
469: }
470: return(retval);
471: }
472:
473: int ciolib_ungetmouse(struct mouse_event *mevent)
474: {
475: int retval=0;
476: struct mouse_event *me;
477:
478: if((me=(struct mouse_event *)malloc(sizeof(struct mouse_event)))==NULL)
479: return(-1);
480: memcpy(me,mevent,sizeof(struct mouse_event));
481: pthread_mutex_lock(&out_mutex);
482: listAddNode(&state.output,me,FIRST_NODE);
483: pthread_mutex_unlock(&out_mutex);
484: return(0);
485: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.