chiark / gitweb /
xduplic-copier: update copyright date and licence to gplv3+
[chiark-utils.git] / cprogs / xduplic-copier.c
1 /*
2  * Copyright (C) 2002,2013 Ian Jackson <ian@chiark.greenend.org.uk>
3  *
4  * This is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 3,
7  * or (at your option) any later version.
8  *
9  * This is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <X11/Xlib.h>
19 #include <X11/keysym.h>
20 #include <X11/cursorfont.h>
21 #include <X11/cursorfont.h>
22 #include <X11/Xmu/WinUtil.h>
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <assert.h>
27
28 static Display *display;
29 static int selecting, l1_x, l1_y;
30 static char stringbuf[20];
31 static unsigned long c_black, c_white, c_red, c_yellow;
32 static Window w, root;
33 static Cursor cursor;
34 static GC gc;
35 static Colormap cmap;
36
37 struct wnode {
38   struct wnode *next;
39   Window w;
40 } *headwn;
41
42 static unsigned long getcolour(const char *name, int def) {
43   Status st;
44   XColor screen_def, exact_def;
45   st= XAllocNamedColor(display,cmap,name,&screen_def,&exact_def);
46   fprintf(stdout,"name %s pixel %lu\n",name,screen_def.pixel);
47   return st ? screen_def.pixel : def;
48 }
49
50 static void beep(void) { XBell(display,100); }
51
52 /*
53  *  While selecting:
54  *                   Left                         Right
55  *    application     select                       deselect
56  *    root            start typing                 deselect all
57  *    xduplic         start typing                 quit
58  *
59  *  While typing:
60  *                   Left                         Right
61  *    xduplic         start selecting              quit
62  *
63  *  Colours:
64  *
65  *    While typing:                   yellow on black
66  *    While selecting:                white on black
67  *    Idle (typing into nowhere):     red on black
68  */
69
70 static void redisplay(void) {
71   XClearWindow(display,w);
72   XDrawString(display,w,gc, l1_x,l1_y, stringbuf,strlen(stringbuf));
73 }
74
75 static void restatus(void) {
76   XGCValues v;
77   int count;
78   struct wnode *own;
79
80   v.foreground= (selecting ? c_white :
81                  headwn ? c_yellow :
82                  c_red);
83   
84   XChangeGC(display,gc,
85             GCForeground,
86             &v);
87   XClearWindow(display,w);
88
89   for (count=0, own=headwn; own; own=own->next) count++;
90   snprintf(stringbuf,sizeof(stringbuf),
91            "%c %d",
92            selecting ? 'S' :
93            headwn ? 'T' : 'i',
94            count);
95   redisplay();
96 }
97
98 static void stopselecting(void) {
99   XUngrabPointer(display,CurrentTime);
100   selecting= 0;
101   restatus();
102 }
103
104 static void startselecting(void) {
105   Status st;
106   st= XGrabPointer(display,root,True,
107                    ButtonPressMask,GrabModeAsync,
108                    GrabModeAsync,None,cursor,CurrentTime);
109   if (st != Success) beep();
110   else selecting= 1;
111   restatus();
112 }
113
114 static void buttonpress(XButtonEvent *e) {
115   struct wnode *own, **ownp, *ownn;
116   int rightbutton;
117   Window sw;
118
119   switch (e->button) {
120   case Button1: rightbutton=0; break;
121   case Button3: rightbutton=1; break;
122   default: return;
123   }
124
125   fprintf(stdout,"button right=%d in=%lx sub=%lx (w=%lx root=%lx)\n",
126           rightbutton, (unsigned long)e->window, (unsigned long)e->subwindow,
127           (unsigned long)w, (unsigned long)e->root);
128
129   if (e->window == w) {
130     if (rightbutton) _exit(0);
131     if (selecting) {
132       stopselecting();
133       /* move pointer to where it already is, just in case wm is confused */
134       XWarpPointer(display,None,root, 0,0,0,0, e->x_root,e->y_root);
135     } else {
136       startselecting();
137     }
138     return;
139   }
140
141   if (!selecting) return;
142
143   if (e->window != e->root) return;
144
145   sw= XmuClientWindow(display, e->subwindow);
146   if (!sw) {
147     if (!rightbutton) {
148       stopselecting();
149     } else {
150       if (!headwn) { beep(); return; }
151       for (own=headwn; own; own=ownn) {
152         ownn= own->next;
153         free(own);
154       }
155       headwn= 0;
156       restatus();
157     }
158     return;
159   }
160
161   if (sw == w) { beep(); return; }
162
163   for (ownp=&headwn;
164        (own=(*ownp)) && own->w != sw;
165        ownp= &(*ownp)->next);
166   
167   if (!rightbutton) {
168     
169     if (own) { beep(); return; }
170     own= malloc(sizeof(*own)); if (!own) { perror("malloc"); exit(-1); }
171     own->w= sw;
172     own->next= headwn;
173     headwn= own;
174
175   } else {
176
177     if (!own) { beep(); return; }
178     *ownp= own->next;
179     free(own);
180
181   }
182
183   restatus();
184 }
185
186 static void keypress(XKeyEvent *e) {
187   Status st;
188   struct wnode *own;
189   unsigned long mask;
190   
191   if (selecting) {
192     fprintf(stdout,"key type %d serial %lu (send %d) "
193             "window %lx root %lx sub %lx time %lx @%dx%d (%dx%dabs) "
194             "state %x keycode %u same %d\n",
195             e->type, e->serial, (int)e->send_event,
196             (unsigned long)e->window,
197             (unsigned long)e->root,
198             (unsigned long)e->subwindow,
199             (unsigned long)e->time,
200             e->x,e->y, e->x_root,e->y_root,
201             e->state, e->keycode, (int)e->same_screen);
202     if (XKeycodeToKeysym(display, e->keycode, 0) == XK_q) _exit(1);
203     beep(); return;
204   }
205   for (own=headwn; own; own=own->next) {
206     mask= (e->type == KeyPress ? KeyPressMask :
207            e->type == KeyRelease ? KeyReleaseMask :
208            KeyPressMask|KeyReleaseMask);
209     e->window= own->w;
210     e->subwindow= None;
211     e->send_event= True;
212     st= XSendEvent(display,own->w,True,mask,(XEvent*)e);
213     if (st != Success) {
214       fprintf(stdout,"sendevent to %lx %d mask %lx\n",
215               (unsigned long)own->w, st, mask);
216     }
217   }
218 }
219
220 static void expose(XExposeEvent *e) {
221   if (e->count) return;
222   redisplay();
223 }
224
225 int main(int argc, const char **argv) {
226   XEvent e;
227   XGCValues gcv;
228   XSetWindowAttributes wv;
229   int screen, direction, ascent, descent, l1_width, l1_height;
230   XCharStruct overall;
231   Font font;
232
233   display= XOpenDisplay(0);
234   screen= DefaultScreen(display);
235   cmap= DefaultColormap(display,screen);
236   root= DefaultRootWindow(display);
237   
238   c_black=   getcolour("black",  0);
239   c_white=   getcolour("white",  1);
240   c_yellow=  getcolour("yellow", c_white);
241   c_red=     getcolour("red",    c_white);
242
243   cursor= XCreateFontCursor(display,XC_crosshair);
244
245   wv.event_mask= KeyPressMask|KeyReleaseMask|ButtonPressMask|ExposureMask;
246   w= XCreateWindow(display, root,
247                    0,0, 50,21, 0,DefaultDepth(display,screen),
248                    InputOutput, DefaultVisual(display,screen),
249                    CWEventMask, &wv);
250
251   font= XLoadFont(display,"fixed");
252
253   gcv.background= c_black;
254   gcv.font=       font;
255   gc= XCreateGC(display,w,GCBackground|GCFont,&gcv);
256
257   XQueryTextExtents(display,font, "SIT 0689", 8,
258                     &direction,&ascent,&descent,&overall);
259   l1_width= overall.lbearing + overall.rbearing;
260   l1_x= overall.lbearing;
261   l1_y= ascent;
262   l1_height= descent+ascent;
263
264   XResizeWindow(display,w, l1_width,l1_height);
265   XSetWindowBackground(display,w,c_black);
266   
267   XMapWindow(display,w);
268   restatus();
269   
270   for (;;) {
271     XNextEvent(display,&e);
272     fprintf(stdout,"selecting = %d; event type = %lu\n",
273             selecting, (unsigned long)e.type);
274     switch (e.type) {
275     case Expose:                     expose(&e.xexpose);       break;
276     case ButtonPress:                buttonpress(&e.xbutton);  break;
277     case KeyPress: case KeyRelease:  keypress(&e.xkey);        break;
278     }
279   }
280 }