chiark / gitweb /
fishdescriptor: wip utility
[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 #include <X11/XKBlib.h>
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <assert.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 static Display *display;
32 static int selecting, l1_x, l1_y;
33 static char stringbuf[20];
34 static unsigned long c_black, c_white, c_red, c_yellow;
35 static Window w, root;
36 static Cursor cursor;
37 static GC gc;
38 static Colormap cmap;
39
40 struct wnode {
41   struct wnode *next;
42   Window w;
43 } *headwn;
44
45 static unsigned long getcolour(const char *name, int def) {
46   Status st;
47   XColor screen_def, exact_def;
48   st= XAllocNamedColor(display,cmap,name,&screen_def,&exact_def);
49   fprintf(stdout,"name %s pixel %lu\n",name,screen_def.pixel);
50   return st ? screen_def.pixel : def;
51 }
52
53 static void beep(void) { XBell(display,100); }
54
55 /*
56  *  While selecting:
57  *                   Left                         Right
58  *    application     select                       deselect
59  *    root            start typing                 deselect all
60  *    xduplic         start typing                 quit
61  *
62  *  While typing:
63  *                   Left                         Right
64  *    xduplic         start selecting              quit
65  *
66  *  Colours:
67  *
68  *    While typing:                   yellow on black
69  *    While selecting:                white on black
70  *    Idle (typing into nowhere):     red on black
71  */
72
73 static void redisplay(void) {
74   XClearWindow(display,w);
75   XDrawString(display,w,gc, l1_x,l1_y, stringbuf,strlen(stringbuf));
76 }
77
78 static void restatus(void) {
79   XGCValues v;
80   int count;
81   struct wnode *own;
82
83   v.foreground= (selecting ? c_white :
84                  headwn ? c_yellow :
85                  c_red);
86   
87   XChangeGC(display,gc,
88             GCForeground,
89             &v);
90   XClearWindow(display,w);
91
92   for (count=0, own=headwn; own; own=own->next) count++;
93   snprintf(stringbuf,sizeof(stringbuf),
94            "%c %d",
95            selecting ? 'S' :
96            headwn ? 'T' : 'i',
97            count);
98   redisplay();
99 }
100
101 static void stopselecting(void) {
102   XUngrabPointer(display,CurrentTime);
103   selecting= 0;
104   restatus();
105 }
106
107 static void startselecting(void) {
108   Status st;
109   st= XGrabPointer(display,root,True,
110                    ButtonPressMask,GrabModeAsync,
111                    GrabModeAsync,None,cursor,CurrentTime);
112   if (st != Success) beep();
113   else selecting= 1;
114   restatus();
115 }
116
117 static void buttonpress(XButtonEvent *e) {
118   struct wnode *own, **ownp, *ownn;
119   int rightbutton;
120   Window sw;
121
122   switch (e->button) {
123   case Button1: rightbutton=0; break;
124   case Button3: rightbutton=1; break;
125   default: return;
126   }
127
128   fprintf(stdout,"button right=%d in=%lx sub=%lx (w=%lx root=%lx)\n",
129           rightbutton, (unsigned long)e->window, (unsigned long)e->subwindow,
130           (unsigned long)w, (unsigned long)e->root);
131
132   if (e->window == w) {
133     if (rightbutton) _exit(0);
134     if (selecting) {
135       stopselecting();
136       /* move pointer to where it already is, just in case wm is confused */
137       XWarpPointer(display,None,root, 0,0,0,0, e->x_root,e->y_root);
138     } else {
139       startselecting();
140     }
141     return;
142   }
143
144   if (!selecting) return;
145
146   if (e->window != e->root) return;
147
148   if (!e->subwindow) {
149     if (!rightbutton) {
150       stopselecting();
151     } else {
152       if (!headwn) { beep(); return; }
153       for (own=headwn; own; own=ownn) {
154         ownn= own->next;
155         free(own);
156       }
157       headwn= 0;
158       restatus();
159     }
160     return;
161   }
162
163   sw= XmuClientWindow(display, e->subwindow);
164
165   if (sw == w) { beep(); return; }
166
167   for (ownp=&headwn;
168        (own=(*ownp)) && own->w != sw;
169        ownp= &(*ownp)->next);
170   
171   if (!rightbutton) {
172     
173     if (own) { beep(); return; }
174     own= malloc(sizeof(*own)); if (!own) { perror("malloc"); exit(-1); }
175     own->w= sw;
176     own->next= headwn;
177     headwn= own;
178
179   } else {
180
181     if (!own) { beep(); return; }
182     *ownp= own->next;
183     free(own);
184
185   }
186
187   restatus();
188 }
189
190 static void keypress(XKeyEvent *e) {
191   Status st;
192   struct wnode *own;
193   unsigned long mask;
194   
195   if (selecting) {
196     fprintf(stdout,"key type %d serial %lu (send %d) "
197             "window %lx root %lx sub %lx time %lx @%dx%d (%dx%dabs) "
198             "state %x keycode %u same %d\n",
199             e->type, e->serial, (int)e->send_event,
200             (unsigned long)e->window,
201             (unsigned long)e->root,
202             (unsigned long)e->subwindow,
203             (unsigned long)e->time,
204             e->x,e->y, e->x_root,e->y_root,
205             e->state, e->keycode, (int)e->same_screen);
206     if (XkbKeycodeToKeysym(display, e->keycode, 0, 0) == XK_q) _exit(1);
207     beep(); return;
208   }
209   for (own=headwn; own; own=own->next) {
210     mask= (e->type == KeyPress ? KeyPressMask :
211            e->type == KeyRelease ? KeyReleaseMask :
212            KeyPressMask|KeyReleaseMask);
213     e->window= own->w;
214     e->subwindow= None;
215     e->send_event= True;
216     st= XSendEvent(display,own->w,True,mask,(XEvent*)e);
217     if (st != Success) {
218       fprintf(stdout,"sendevent to %lx %d mask %lx\n",
219               (unsigned long)own->w, st, mask);
220     }
221   }
222 }
223
224 static void expose(XExposeEvent *e) {
225   if (e->count) return;
226   redisplay();
227 }
228
229 int main(int argc, const char **argv) {
230   XEvent e;
231   XGCValues gcv;
232   XSetWindowAttributes wv;
233   int screen, direction, ascent, descent, l1_width, l1_height;
234   XCharStruct overall;
235   Font font;
236
237   display= XOpenDisplay(0);
238   if (!display) { fputs("XOpenDisplay failed\n",stderr); exit(-1); }
239   screen= DefaultScreen(display);
240   cmap= DefaultColormap(display,screen);
241   root= DefaultRootWindow(display);
242   
243   c_black=   getcolour("black",  0);
244   c_white=   getcolour("white",  1);
245   c_yellow=  getcolour("yellow", c_white);
246   c_red=     getcolour("red",    c_white);
247
248   cursor= XCreateFontCursor(display,XC_crosshair);
249
250   wv.event_mask= KeyPressMask|KeyReleaseMask|ButtonPressMask|ExposureMask;
251   w= XCreateWindow(display, root,
252                    0,0, 50,21, 0,DefaultDepth(display,screen),
253                    InputOutput, DefaultVisual(display,screen),
254                    CWEventMask, &wv);
255
256   font= XLoadFont(display,"fixed");
257
258   gcv.background= c_black;
259   gcv.font=       font;
260   gc= XCreateGC(display,w,GCBackground|GCFont,&gcv);
261
262   XQueryTextExtents(display,font, "SIT 0689", 8,
263                     &direction,&ascent,&descent,&overall);
264   l1_width= overall.lbearing + overall.rbearing;
265   l1_x= overall.lbearing;
266   l1_y= ascent;
267   l1_height= descent+ascent;
268
269   XResizeWindow(display,w, l1_width,l1_height);
270   XSetWindowBackground(display,w,c_black);
271   
272   XMapWindow(display,w);
273   restatus();
274   
275   for (;;) {
276     XNextEvent(display,&e);
277     fprintf(stdout,"selecting = %d; event type = %lu\n",
278             selecting, (unsigned long)e.type);
279     switch (e.type) {
280     case Expose:                     expose(&e.xexpose);       break;
281     case ButtonPress:                buttonpress(&e.xbutton);  break;
282     case KeyPress: case KeyRelease:  keypress(&e.xkey);        break;
283     }
284   }
285 }