]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - xcut.c
c8119b16691f26726925a252cf6c07c30df40529
[1ts-debian.git] / xcut.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It is one of the source files comprising zwgc, the Zephyr WindowGram
3  * client.
4  *
5  *      Created by:     Marc Horowitz <marc@athena.mit.edu>
6  *
7  *      $Id: xcut.c 2630 2011-02-02 05:26:26Z kcr@ATHENA.MIT.EDU $
8  *
9  *      Copyright (c) 1989 by the Massachusetts Institute of Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h".
12  */
13
14 #include <sysdep.h>
15
16 #if (!defined(lint) && !defined(SABER))
17 static const char rcsid_xcut_c[] = "$Id: xcut.c 2630 2011-02-02 05:26:26Z kcr@ATHENA.MIT.EDU $";
18 #endif
19
20 #include <zephyr/mit-copyright.h>
21
22 /****************************************************************************/
23 /*                                                                          */
24 /*                    Code to deal with handling X events:                  */
25 /*                                                                          */
26 /****************************************************************************/
27
28 #ifndef X_DISPLAY_MISSING
29
30 #include <X11/Xlib.h>
31 #include <X11/Xutil.h>
32 #include <zephyr/zephyr.h>
33 #include "new_memory.h"
34 #include "new_string.h"
35 #include "X_gram.h"
36 #include "zwgc.h"
37 #include "xselect.h"
38 #include "xmark.h"
39 #include "error.h"
40 #include "xrevstack.h"
41 #include "X_driver.h"
42 #include "xcut.h"
43 #ifdef CMU_ZWGCPLUS
44 #include "plus.h"
45 #include "variables.h"
46 #endif
47
48 /*
49  *
50  */
51
52 extern long ttl;
53
54 static char *selected_text=NULL;
55 static Window selecting_in = 0;
56
57 char *
58 getSelectedText(void)
59 {
60    return(selected_text);
61 }
62
63 #ifdef notdef
64 static string
65 x_gram_to_string(x_gram *gram)
66 {
67     int i, index, len;
68     int last_y = -1;
69     string temp;
70     string text_so_far = string_Copy("");
71     char *text;
72
73     text = gram->text;
74     for (i=0; i<gram->numblocks; i++) {
75         if (last_y != -1 && last_y != gram->blocks[i].y)
76           text_so_far = string_Concat2(text_so_far, "\n");
77         index = gram->blocks[i].strindex;
78         len = gram->blocks[i].strlen;
79         temp = string_CreateFromData(text+index, len);
80         text_so_far = string_Concat2(text_so_far, temp);
81         free(temp);
82         last_y = gram->blocks[i].y;
83     }
84
85     text_so_far = string_Concat2(text_so_far, "\n");
86     return(text_so_far);
87 }
88 #endif
89
90 /*
91  *
92  */
93
94 /*ARGSUSED*/
95 Bool
96 isShiftButton1(Display *dpy,
97                XEvent *event,
98                char *arg)
99 {
100    return(event->xbutton.state & (ShiftMask|Button1Mask));
101 }
102
103 /*ARGSUSED*/
104 Bool
105 isShiftButton3(Display *dpy,
106                XEvent *event,
107                char *arg)
108 {
109    return(event->xbutton.state & (ShiftMask|Button3Mask));
110 }
111
112 void
113 getLastEvent(Display *dpy,
114              unsigned int state,
115              XEvent *event)
116 {
117    XEvent xev;
118
119    if (state & Button1Mask) {
120       while(XCheckIfEvent(dpy,&xev,isShiftButton1,NULL))
121          *event=xev;
122    } else if (state & Button3Mask) {
123       while(XCheckIfEvent(dpy,&xev,isShiftButton3,NULL))
124          *event=xev;
125    }
126 }
127
128 void
129 xunmark(Display *dpy,
130         Window w,
131         x_gram *gram,
132         XContext desc_context)
133 {
134    if (gram == NULL)
135      if (XFindContext(dpy, w, desc_context, (XPointer *) &gram))
136        return;
137
138    xmarkClear();
139    xmarkRedraw(dpy,w,gram,XMARK_REDRAW_OLD);
140 }
141
142 /* This is out here so xdestroygram can get at it */
143
144 #define PRESSOP_NONE 0  /* nothing */
145 #define PRESSOP_KILL 1  /* normal click */
146 #define PRESSOP_SEL  2  /* shift left */
147 #define PRESSOP_EXT  3  /* shift right */
148 #define PRESSOP_NUKE 4 /* ctrl */
149 #define PRESSOP_STOP 5  /* pressop cancelled by moving out of window */
150
151 static int current_pressop = PRESSOP_NONE;
152
153 void
154 xdestroygram(Display *dpy,
155              Window w,
156              XContext desc_context,
157              x_gram *gram)
158 {
159     struct timeval now;
160     int i;
161
162     gettimeofday(&now,NULL);
163     if ((gram->can_die.tv_sec == 0) ||
164         (gram->can_die.tv_sec > now.tv_sec) ||
165         ((gram->can_die.tv_sec == now.tv_sec) &&
166          (gram->can_die.tv_usec > now.tv_usec)))
167         return;
168
169     if (w == selecting_in) {
170         selecting_in = 0;
171         xmarkClear();
172     }
173     current_pressop = PRESSOP_NONE;
174     XDeleteContext(dpy, w, desc_context);
175     XDestroyWindow(dpy, w);
176     delete_gram(gram);
177     free(gram->text);
178     for (i=0; i < gram->numblocks; i++)
179         free(gram->blocks[i].wstr);
180     free(gram->blocks);
181 #ifdef CMU_ZWGCPLUS
182     if (gram->notice)
183       list_del_notice(gram->notice);
184 #endif
185     free(gram);
186
187 #ifdef CMU_ZWGCPLUS
188     XFlush(dpy);
189 #endif
190
191     if (bottom_gram == NULL && unlinked == NULL) {
192        /* flush colormap here */
193     }
194 }
195
196 void
197 xcut(Display *dpy,
198      XEvent *event,
199      XContext desc_context)
200 {
201     x_gram *gram;
202     Window w = event->xany.window;
203     int changedbound;
204
205     /*
206      * If event is for a window that's not ours anymore (say we're
207      * in the process of deleting it...), ignore it:
208      */
209     if (XFindContext(dpy, w, desc_context, (XPointer *) &gram))
210       return;
211
212     /*
213      * Dispatch on the event type:
214      */
215     switch(event->type) {
216 #ifdef CMU_ZWGCPLUS
217     case KeyPress:
218       {
219         char c;
220         char *plusvar;
221         int res, metaflag;
222         res = XLookupString(&(event->xkey), &c, 1, NULL, NULL);
223         metaflag = event->xkey.state & Mod1Mask;
224
225         /* Recheck if zwgcplus is turned on;
226          *  Zephyr variables override zwgc variables
227          */
228
229         zwgcplus = 1;
230         plusvar = ZGetVariable("zwgcplus")
231             ? ZGetVariable("zwgcplus") : var_get_variable("zwgcplus");
232
233         if ((plusvar[0]=='\0') || (strcmp(plusvar,"no") == 0))
234           zwgcplus = 0;
235         else {
236           if (strcmp(plusvar,"no") == 0)
237             zwgcplus = 0;
238           if (strcmp(plusvar,"new") == 0)
239             zwgcplus = 2;
240         }
241
242         if (res != 0 && zwgcplus != 0)
243           plus_retry_notice(gram->notice, c, metaflag);
244       }
245       break;
246 #endif
247       case ClientMessage:
248         if ((event->xclient.message_type == XA_WM_PROTOCOLS) &&
249             (event->xclient.format == 32) &&
250             (event->xclient.data.l[0] == XA_WM_DELETE_WINDOW))
251             xdestroygram(dpy,w,desc_context,gram);
252         break;
253
254       case MapNotify:
255         /* I don't like using the local time, but MapNotify events don't
256          * come with a timestamp, and there's no way to query the server
257          */
258
259         if (gram->can_die.tv_sec == 0) {
260             gettimeofday(&(gram->can_die),NULL);
261             gram->can_die.tv_sec += (int) (ttl/1000);
262             gram->can_die.tv_usec += (ttl%1000)*1000;
263         }
264         break;
265
266       case UnmapNotify:
267         unlink_gram(gram);
268         break;
269
270       case LeaveNotify:
271         if (current_pressop == PRESSOP_KILL ||
272             current_pressop == PRESSOP_NUKE)
273            current_pressop = PRESSOP_STOP;
274         break;
275
276       case MotionNotify:
277         if (current_pressop == PRESSOP_SEL) {
278            /*     getLastEvent(dpy,Button1Mask,event); */
279            changedbound=xmarkExtendFromFirst(gram,event->xmotion.x,
280                                              event->xmotion.y);
281            xmarkRedraw(dpy,w,gram,changedbound);
282         } else if (current_pressop == PRESSOP_EXT) {
283            /*     getLastEvent(dpy,Button3Mask,event); */
284            changedbound=xmarkExtendFromNearest(gram,event->xmotion.x,
285                                                event->xmotion.y);
286            xmarkRedraw(dpy,w,gram,changedbound);
287         } 
288         break;
289
290       case ButtonPress:
291         if (current_pressop != PRESSOP_NONE) {
292            current_pressop = PRESSOP_STOP;
293         } else if ((event->xbutton.button==Button4 ||
294                     event->xbutton.button==Button5) &&
295                    !get_bool_resource("scrollDelete","ScrollDelete",0)) {
296            /* Ignore scroll wheel movement. */
297            break;
298         } else if ( (event->xbutton.state)&ShiftMask ) {
299            if (event->xbutton.button==Button1) {
300               if (selecting_in)
301                  xunmark(dpy,selecting_in,NULL,desc_context);
302               if (selected_text) free(selected_text);
303               selected_text = NULL;
304               if (! xselGetOwnership(dpy,w,event->xbutton.time)) {
305                  XBell(dpy,0);
306                  ERROR("Unable to get ownership of PRIMARY selection.\n");
307                  selecting_in = 0;
308                  current_pressop = PRESSOP_STOP;
309               } else {
310                  selecting_in = w;
311                  xmarkStart(gram,event->xbutton.x,event->xbutton.y);
312                  current_pressop = PRESSOP_SEL;
313               }
314            } else if ((event->xbutton.button==Button3) &&
315                       (w == selecting_in)) {
316               if (selected_text) free(selected_text);
317               selected_text = NULL;
318               changedbound=xmarkExtendFromNearest(gram,event->xbutton.x,
319                                                   event->xbutton.y);
320               xmarkRedraw(dpy,w,gram,changedbound);
321               selected_text = xmarkGetText();
322               /* this is ok, since to get here, the selection must be owned */
323               current_pressop = PRESSOP_EXT;
324 #ifdef CMU_ZWGCPLUS
325               if (selected_text)
326                 XStoreBytes(dpy, selected_text, strlen(selected_text)+1);
327 #endif
328            }
329         } else if ( (event->xbutton.state)&ControlMask ) {
330            current_pressop = PRESSOP_NUKE;
331         } else {
332            current_pressop = PRESSOP_KILL;
333         }
334         break;
335
336       case ButtonRelease:
337         if (current_pressop == PRESSOP_KILL) {
338            xdestroygram(dpy,w,desc_context,gram);
339         } else if (current_pressop == PRESSOP_SEL ||
340                    current_pressop == PRESSOP_EXT) {
341            if (selected_text) free(selected_text);
342            selected_text = xmarkGetText();
343 #ifdef CMU_ZWGCPLUS
344            if (selected_text)
345              XStoreBytes(dpy, selected_text, strlen(selected_text)+1);
346 #endif
347         } else if (current_pressop == PRESSOP_NUKE) {
348            XWindowAttributes wa;
349            int gx,gy;
350            Window temp;
351            x_gram *next;
352
353            for (gram = bottom_gram ; gram ; gram = next) {
354               XGetWindowAttributes(dpy,gram->w,&wa);
355               XTranslateCoordinates(dpy,gram->w,wa.root,0,0,&gx,&gy,
356                                     &temp);
357
358               next = gram->above;
359
360               if ((wa.map_state == IsViewable) &&
361                   (gx <= event->xbutton.x_root) &&
362                   (event->xbutton.x_root < gx+wa.width) &&
363                   (gy <= event->xbutton.y_root) &&
364                   (event->xbutton.y_root < gy+wa.height)) {
365                  xdestroygram(dpy,gram->w,desc_context,gram);
366               }
367            }
368            for (gram = unlinked ; gram ; gram = next) {
369               XGetWindowAttributes(dpy,gram->w,&wa);
370               XTranslateCoordinates(dpy,gram->w,wa.root,0,0,&gx,&gy,
371                                     &temp);
372
373               next = gram->above;
374
375               if ((wa.map_state == IsViewable) &&
376                   (gx <= event->xbutton.x_root) &&
377                   (event->xbutton.x_root < gx+wa.width) &&
378                   (gy <= event->xbutton.y_root) &&
379                   (event->xbutton.y_root < gy+wa.height)) {
380                  xdestroygram(dpy,gram->w,desc_context,gram);
381               }
382            }
383         }
384         current_pressop = PRESSOP_NONE;
385         break;
386
387      case SelectionRequest:
388        xselProcessSelection(dpy,w,event);
389        break;
390
391      case SelectionClear:
392        xselOwnershipLost(event->xselectionclear.time);
393        if (w == selecting_in) {
394           selecting_in = 0;
395           xunmark(dpy,w,gram,desc_context);
396           if (selected_text) free(selected_text);
397           selected_text = NULL;
398        }
399        break;
400
401 #ifdef notdef
402       case ConfigureNotify:
403 #ifdef DEBUG
404         if (zwgc_debug)
405           printf("ConfigureNotify received for wid %lx above wid %lx\n",
406                  (long) w,(long) event->xconfigure.above);
407 #endif
408         if (gram->above==gram) {
409            /* a new zgram.  Straight to the bottom! */
410            add_to_bottom(gram);
411         } else if (event->xconfigure.above)  {
412            /* some zgram was pulled to the top */
413            pull_to_top(gram);
414         } else {
415            /* Some zgram was pushed to the bottom */
416            push_to_bottom(gram);
417         }
418         /* Note that there is no option to configure a zgram to the middle */
419         break;
420 #endif
421       default:
422         break;
423     }
424
425     XFlush(dpy);
426 }
427
428 #endif
429