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