1 /* $Header: /cvsroot/lesstif/lesstif/test/extra/daniel/test1.c,v 1.2 2002/05/15 10:55:06 amai Exp $ */
2 /***********************************************************/
3 /* Copyright 1996 Daniel Dardailler.
4 Permission to use, copy, modify, distribute, and sell this software
5 for any purpose is hereby granted without fee, provided that the above
6 copyright notice appear in all copies and that both that copyright
7 notice and this permission notice appear in supporting documentation,
8 and that the name of Daniel Dardailler not be used in advertising or
9 publicity pertaining to distribution of the software without specific,
10 written prior permission.  Daniel Dardailler makes no representations
11 about the suitability of this software for any purpose.  It is
12 provided "as is" without express or implied warranty.
13 ************************************************************/
14 
15 #include <stdlib.h>
16 #include <stdio.h>
17 
18 #include <X11/Xlib.h>
19 #include <X11/Intrinsic.h>
20 #include <X11/StringDefs.h>
21 #include <X11/Xos.h>
22 #include <X11/Xatom.h>
23 #include <X11/Shell.h>
24 #include <X11/Xaw/Label.h>
25 #include <X11/Xaw/Box.h>
26 #include <X11/cursorfont.h>
27 #include "Dnd.h"
28 
29 /************************************************************
30   This demo program creates two Aw labels, one is draggrable,
31   the other is a drop site (change the label when you drop)
32   It does a bunch of tracing.
33 ************************************************************/
34 
35 
36 static void StartDrag(Widget w, XEvent *event,
37 		      String *params, Cardinal *num_params);
38 static Atom Dnd_wm_state, Dnd_selection, Dnd_transfer_success ;
39 static Cursor valid_cursor, invalid_cursor;
40 static String drag_trans = "<Btn2Down>: StartDrag()" ;
41 static Atom my_targets[] = { XA_STRING } ;
42 static Atom cv_targets[3] ;
43 static XtPointer closures[3] ;
44 static Boolean in_drag = False ;
45 static Widget drag_label, drop_label, top_level, box ;
46 static XtActionsRec drag_actions [] = {{"StartDrag", StartDrag}} ;
47 
48 
49 static void
SelectTopLevels(Display * dpy,Window win)50 SelectTopLevels(
51         Display *dpy,
52         Window win)
53 {
54     Window 		root, parent;
55     Window 		*children;
56     unsigned int 	nchildren;
57     int		 	i;
58     Atom 		type = None;
59     int 		format;
60     unsigned long 	nitems, after;
61     unsigned char 	*data;
62 
63     if (win != DefaultRootWindow(dpy)) {
64 	/* only ask property kid of the root */
65 	XGetWindowProperty(dpy, win, Dnd_wm_state , 0, 0, False,
66 			   AnyPropertyType,
67 			   &type, &format, &nitems, &after, &data);
68 	XFree(data);
69     } else {
70 	printf ("WM_STATE on: ");
71     }
72 
73     /* if the window has the WM_STATE property, select D&D events for it */
74     if (type) {
75 	XSelectInput(dpy, win, EnterWindowMask | LeaveWindowMask
76 		     | Button2MotionMask);
77 	printf("%d ",win);
78     } else {
79 	if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren) ||
80 	    (nchildren == 0))
81 	  return ;
82 	for (i = nchildren - 1; i >= 0; i--) {
83 	    SelectTopLevels(dpy, children[i]) ;
84 	}
85 	XFree(children);
86     }
87 
88     if (win == DefaultRootWindow(dpy)) printf("\n----------\n");
89 }
90 
91 static void
ChangeCursorInvalid(Display * dpy,Time time)92 ChangeCursorInvalid(Display * dpy, Time time)
93 {
94     printf("ChangeCursorInvalid\n");
95     XChangeActivePointerGrab(dpy,
96 			     ButtonMotionMask|ButtonReleaseMask|
97 			     EnterWindowMask|LeaveWindowMask,
98 			     invalid_cursor, time);
99 }
100 
101 static void
ChangeCursorValid(Display * dpy,Time time)102 ChangeCursorValid(Display * dpy, Time time)
103 {
104     printf("ChangeCursorValid\n");
105     XChangeActivePointerGrab(dpy,
106 			     ButtonMotionMask|ButtonReleaseMask|
107 			     EnterWindowMask|LeaveWindowMask,
108 			     valid_cursor, time);
109 }
110 
111 
112 static void
StartDrag(Widget w,XEvent * event,String * params,Cardinal * num_params)113 StartDrag(
114 	  Widget	w,
115 	  XEvent	*event,
116 	  String *params, Cardinal *num_params)
117 {
118     printf("StartDrag\n");
119 
120     /* grab the pointer so that we get all crossing events */
121     XGrabPointer(XtDisplay(w), DefaultRootWindow(XtDisplay(w)), True,
122 		 ButtonMotionMask|ButtonReleaseMask|
123 		 EnterWindowMask|LeaveWindowMask,
124 		 GrabModeSync, GrabModeAsync,
125 		 None, invalid_cursor,
126 		 event->xbutton.time);
127 
128     in_drag = True ;
129 
130     XAllowEvents(XtDisplay(w), SyncPointer, event->xbutton.time);
131 }
132 
133 static Boolean
SourceConvert(Widget w,Atom * selection,Atom * target,Atom * type,XtPointer * value,unsigned long * length,int * format)134 SourceConvert(
135         Widget w,
136         Atom *selection,
137         Atom *target,
138         Atom *type,
139         XtPointer *value,
140         unsigned long *length,
141         int *format )
142 {
143     printf("SourceConvert %d %s\n", *target,
144 	   XGetAtomName(XtDisplay(w),*target));
145 
146     if (*target == XA_STRING) {
147 	String label ;
148 	*type   = XA_STRING;
149 	XtVaGetValues(drag_label, XtNlabel, &label, NULL);
150 	*value = XtNewString(label);
151 	*length = strlen(label);
152 	printf("value %s length %d\n", *value, *length);
153 	*format = 8;
154 	return True;
155     } else
156     if (*target == Dnd_transfer_success) {
157 	printf("transfer success\n");
158 	*type   = Dnd_transfer_success;
159 	*length = 0 ;
160 	*value = NULL ;
161 	*format = 32;
162 	return True;
163     } else
164     if (*target == XInternAtom(XtDisplay(w), "DELETE", False)) {
165 	*type = XInternAtom(XtDisplay(w), "NULL", False);
166 	*value = NULL;
167 	*length = 0;
168 	*format = 8;
169 	return True;
170     }
171 
172   return False;
173 }
174 
175 static void
ReceiverConvert(Widget w,XtPointer client_data,Atom * selection,Atom * type,XtPointer val,unsigned long * length,int * format)176 ReceiverConvert(
177         Widget w,
178         XtPointer client_data,
179         Atom *selection,
180         Atom *type,
181         XtPointer val,
182         unsigned long *length,
183         int *format )
184 {
185     printf("ReceiverConvert selection %d %s\n",
186 	   *selection, XGetAtomName(XtDisplay(w), *selection));
187     printf("type %d %s\n",*type,
188 	   (*type)?XGetAtomName(XtDisplay(w), *type):"00");
189     printf("format %d\n",*format);
190     printf("length %d\n",*length);
191 
192     if (*type != 0 && *type != Dnd_transfer_success) {
193 	XtVaSetValues(drop_label, XtNlabel, val, NULL);
194 
195 	/* copy data received in our drop site */
196 	/* indicate success by asking the source to convert
197 	   XmTRANSFER_SUCCESS */
198 	XtGetSelectionValue(w, *selection,
199 			    Dnd_transfer_success, ReceiverConvert, NULL,
200 			    CurrentTime);
201 	/* we should get called again with this target */
202     }
203     /* else, got transfer success back, free whatever we want */
204 
205  /* Note: I get some weird
206     Warning:
207       Name: dd
208       Class: ApplicationShell
209       We lost the drop selection
210     on a (real) Motif initiator side when I get to this point...
211     No time to investigate right now */
212 }
213 
214 int
main(argc,argv)215 main(argc, argv)
216 	int argc;
217 	char **argv;
218 {
219     XtAppContext app_context;
220     unsigned char src_protocol_style = DND_DRAG_DYNAMIC ;
221     char my_dnd_selection_name[30] ;
222     Boolean do_messaging = False, do_drop = False, in_drop_site = False ;
223     Window cur_window = 0 ;
224     Atom * src_targets ;
225     unsigned short num_src_targets ;
226     Position dropx, dropy ;
227     Dimension dropw, droph ;
228 
229     top_level = XtVaAppInitialize(&app_context, "DndTest",
230 				  NULL, 0, &argc, argv, NULL,
231 				  XtNallowShellResize, True, NULL);
232     XtAppAddActions(app_context, (XtActionList)drag_actions,
233 		    XtNumber(drag_actions));
234 
235     /* Init atoms */
236     Dnd_wm_state =   XInternAtom(XtDisplay(top_level), "WM_STATE", False);
237     Dnd_transfer_success =   XInternAtom(XtDisplay(top_level),
238 					 "XmTRANSFER_SUCCESS", False);
239 
240     /* This one needs to be unique for each drag, not just for each client,
241        as there can be race condition with drag transfer not finish while
242        another drag is started */
243     sprintf(my_dnd_selection_name, "_MY_DND_SELECTION_%d", getpid());
244     Dnd_selection =  XInternAtom(XtDisplay(top_level),
245 				 my_dnd_selection_name, False);
246 
247 
248     invalid_cursor = XCreateFontCursor(XtDisplay(top_level),XC_pirate);
249     valid_cursor = XCreateFontCursor(XtDisplay(top_level), XC_target);
250 
251     box = XtCreateManagedWidget("box",
252 				boxWidgetClass, top_level, NULL, 0);
253 
254     drag_label = XtVaCreateManagedWidget("drag",
255 				  labelWidgetClass, box, NULL);
256     XtOverrideTranslations(drag_label, XtParseTranslationTable(drag_trans));
257 
258     drop_label = XtVaCreateManagedWidget("drop",
259 				    labelWidgetClass, box, NULL);
260 
261     XtRealizeWidget(top_level); /* need the windows right below */
262 
263 
264      /* register as a drag source. In real life, one would have to
265 	update the property per drag source, for real target indication */
266     DndWriteSourceProperty(XtDisplay(top_level), XtWindow(top_level),
267 			   Dnd_selection, my_targets, 1);
268 
269     /* gotta be ready to convert our selection - same thing */
270     XtOwnSelection(top_level, Dnd_selection, CurrentTime,
271 		   SourceConvert, NULL, NULL);
272 
273     if (argc > 1) {
274 	if (strcmp(argv[1], "none") == 0)
275 	    src_protocol_style = DND_DRAG_NONE ;
276 	else
277 	if (strcmp(argv[1], "drop_only") == 0)
278 	    src_protocol_style = DND_DRAG_DROP_ONLY;
279 	else
280 	if (strcmp(argv[1], "dynamic") == 0)
281 	    src_protocol_style = DND_DRAG_DYNAMIC ;
282     }
283 
284     /* register the top_level as a drop site too */
285     DndWriteReceiverProperty(XtDisplay(top_level), XtWindow(top_level),
286 			     src_protocol_style);
287 
288     /* really just need to be done on start drag (and tracked too as window
289        are added, destroyed. Motif uses private properties to find these */
290     SelectTopLevels(XtDisplay(top_level),
291 		   DefaultRootWindow(XtDisplay(top_level)));
292 
293     /* for now use my own loop for tracking events directly,
294        rather messy... */
295     while(1) {
296 	XEvent event ;
297 	unsigned char receiv_protocol_style;
298 	XClientMessageEvent cm ;
299 	DndData dnd_data ;
300 	char receiver ;
301 
302 	XtAppNextEvent(app_context, &event);
303 
304 	switch(event.type) {
305 
306 	case EnterNotify:
307 	    if (!in_drag) break ;
308 
309 	    printf("enter win %d sub %d\n", event.xcrossing.window,
310 		   event.xcrossing.subwindow);
311 
312 	    cur_window = event.xcrossing.window ;
313 
314 	    /* just enter a new top_level, check its dnd data */
315 	    DndReadReceiverProperty(event.xcrossing.display,
316 				    event.xcrossing.window,
317 				    &receiv_protocol_style);
318 
319 	    printf("receiv_protocol_style %d\n",receiv_protocol_style);
320 
321 	    if (receiv_protocol_style == DND_DRAG_NONE) {
322 		ChangeCursorInvalid(event.xcrossing.display,
323 				    event.xcrossing.time) ;
324 		do_messaging = False ;
325 		do_drop = False ;
326 	    } else
327 	    if (receiv_protocol_style == DND_DRAG_DROP_ONLY) {
328 		ChangeCursorValid(event.xcrossing.display,
329 				  event.xcrossing.time) ;
330 		do_messaging = False ;
331 		do_drop = True ;
332 	    } else
333             if (receiv_protocol_style == DND_DRAG_DYNAMIC) {
334 
335 		/* we'll get valid visual on drop_site_entered */
336 		ChangeCursorInvalid(event.xcrossing.display,
337 				    event.xcrossing.time) ;
338 
339 		dnd_data.reason = DND_TOP_LEVEL_ENTER ;
340 		dnd_data.time = event.xcrossing.time ;
341 		dnd_data.src_window = XtWindow(top_level);
342 		dnd_data.property = Dnd_selection ;
343 
344 		DndFillClientMessage (event.xcrossing.display,
345 				      cur_window,
346 				      &cm, &dnd_data, 0);
347 
348 		XSendEvent(event.xcrossing.display,
349 			   cur_window, False, 0, (XEvent *)&cm) ;
350 
351 		printf("XSendEvent DND_TOP_LEVEL_ENTER %d\n", cur_window);
352 
353 		do_messaging = True ;
354 		do_drop = False ;
355 	    }
356 	    break ;
357 	case LeaveNotify:
358 	    if (!in_drag || !do_messaging) break ;
359 
360 	    printf("leave win %d sub %d\n", event.xcrossing.window,
361 		   event.xcrossing.subwindow);
362 
363 	    ChangeCursorInvalid(event.xcrossing.display,
364 				event.xcrossing.time) ;
365 
366 	    dnd_data.reason = DND_TOP_LEVEL_LEAVE ;
367 	    dnd_data.time = event.xcrossing.time ;
368 	    dnd_data.src_window = XtWindow(top_level);
369 
370 	    DndFillClientMessage (event.xcrossing.display,
371 				  cur_window,
372 				  &cm, &dnd_data, 0);
373 
374 	    XSendEvent(event.xcrossing.display,
375 		       cur_window, False, 0, (XEvent *)&cm) ;
376 
377 	    printf("XSendEvent DND_TOP_LEVEL_LEAVE %d\n",
378 		   cur_window);
379 
380 	    do_messaging = False ;
381 	    cur_window = 0 ;
382 
383 	    break ;
384 	case MotionNotify:
385 	    if (!in_drag || !do_messaging) break ;
386 
387 	    printf("motion win %d sub %d\n", event.xmotion.window,
388 		   event.xmotion.subwindow);
389 
390 	    dnd_data.reason = DND_DRAG_MOTION ;
391 	    dnd_data.time = event.xmotion.time ;
392 	    dnd_data.operation = DND_MOVE|DND_COPY;
393 	    dnd_data.operations = DND_MOVE|DND_COPY;
394 	    dnd_data.x = event.xmotion.x_root ;
395 	    dnd_data.y = event.xmotion.y_root ;
396 
397 	    DndFillClientMessage (event.xmotion.display,
398 				  cur_window,
399 				  &cm, &dnd_data, 0);
400 
401 	    XSendEvent(event.xmotion.display,
402 		       cur_window, False, 0, (XEvent *)&cm) ;
403 
404 	    printf("XSendEvent DND_DRAG_MOTION %d\n", cur_window);
405 
406 	    break ;
407 	case ButtonRelease:
408 	    if (!in_drag) break ;
409 
410 	    printf("release in %d\n",cur_window);
411 	    XUngrabPointer(XtDisplay(top_level), event.xbutton.time);
412 	    in_drag = False ;
413 
414 	    if (do_drop) {
415 
416 		dnd_data.reason = DND_DROP_START ;
417 		dnd_data.time = event.xbutton.time ;
418 		dnd_data.operation = DND_MOVE|DND_COPY;
419 		dnd_data.operations = DND_MOVE|DND_COPY;
420 		dnd_data.src_window = XtWindow(top_level);
421 		dnd_data.property = Dnd_selection ;
422 
423 		DndFillClientMessage (event.xbutton.display,
424 				      cur_window,
425 				      &cm, &dnd_data, 0);
426 
427 		XSendEvent(event.xbutton.display,
428 			  cur_window, False, 0, (XEvent *)&cm) ;
429 
430 		printf("XSendEvent DND_DROP_START %d\n", cur_window);
431 
432 		do_messaging = False ;
433 		do_drop = False ;
434 	    }
435 	    break ;
436 	case ClientMessage:
437 
438 	    if (!(DndParseClientMessage ((XClientMessageEvent*)&event,
439 					 &dnd_data, &receiver))) {
440 		printf("not a valid Dnd client message\n");
441 		break ;
442 	    }
443 
444 	    if (dnd_data.reason == DND_DRAG_MOTION) {
445 		if (receiver) {
446 		    printf("receiver echoing drag motion\n");
447 		    /* don't need to do anything, really, since
448 		       we're not supporting better_x,y position */
449 		} else {
450 		    printf("source sending a drag motion\n");
451 		    /* check if in drop site, and depending on the state,
452 		       send a drop site enter or drop site leave or echo */
453 		    printf("x %d y %d\n",dnd_data.x, dnd_data.y);
454 		    if (dnd_data.x > dropx && dnd_data.y > dropy &&
455 			dnd_data.x < dropx + (Position)dropw &&
456 			dnd_data.y < dropy + (Position)droph) {
457 			if (!in_drop_site) {
458 			    in_drop_site = True ;
459 
460 			    dnd_data.reason = DND_DROP_SITE_ENTER ;
461 			    dnd_data.time = CurrentTime ;
462 			    dnd_data.operation = DND_MOVE|DND_COPY;
463 			    dnd_data.operations = DND_MOVE|DND_COPY;
464 
465 			    DndFillClientMessage (event.xclient.display,
466 						  cur_window,
467 						  &cm, &dnd_data, 0);
468 
469 			    XSendEvent(event.xbutton.display,
470 				       cur_window, False, 0,
471 				       (XEvent *)&cm) ;
472 
473 
474 			    printf("XSendEvent DND_DROP_SITE_ENTER %d\n",
475 				   cur_window);
476 
477 			} else {
478 			    dnd_data.reason = DND_DRAG_MOTION ;
479 			    dnd_data.time = CurrentTime ;
480 			    dnd_data.operation = DND_MOVE|DND_COPY;
481 			    dnd_data.operations = DND_MOVE|DND_COPY;
482 
483 			    DndFillClientMessage (event.xclient.display,
484 						  cur_window,
485 						   &cm, &dnd_data, 0);
486 
487 			    XSendEvent(event.xbutton.display,
488 				       cur_window, False, 0,
489 				       (XEvent *)&cm) ;
490 
491 
492 			    printf("XSendEvent DND_DRAG_MOTION %d\n",
493 				   cur_window);
494 
495 			}
496 		    } else {
497 			if (in_drop_site) {
498 			    in_drop_site = False ;
499 
500 			    dnd_data.reason = DND_DROP_SITE_LEAVE ;
501 			    dnd_data.time = CurrentTime ;
502 
503 			    DndFillClientMessage (event.xclient.display,
504 						  cur_window,
505 						  &cm, &dnd_data, 0);
506 
507 			    XSendEvent(event.xbutton.display,
508 				       cur_window, False, 0,
509 				       (XEvent *)&cm) ;
510 
511 
512 			    printf("XSendEvent DND_DROP_SITE_LEAVE %d\n",
513 				   cur_window);
514 			}
515 		    }
516 		}
517 	    } else
518 	    if (dnd_data.reason == DND_TOP_LEVEL_ENTER) {
519 		/* get the size of our drop site for later use */
520 		XtVaGetValues(drop_label,
521 			      XtNwidth, &dropw, XtNheight, &droph, NULL);
522 		XtTranslateCoords(drop_label, 0, 0, &dropx, &dropy);
523 		printf("x %d y %d w %d h %d\n", dropx, dropy, dropw, droph);
524 
525 		printf("source sending a top level enter %d\n",
526 		       dnd_data.src_window);
527 
528 		cur_window = dnd_data.src_window ;
529 
530 		/* no answer needed, just read source property */
531 		DndReadSourceProperty (event.xclient.display,
532 				       cur_window,
533 				       dnd_data.property,
534 				       &src_targets, &num_src_targets);
535 		printf("src_targets %d num_src_targets %d\n",
536 		       src_targets[0], num_src_targets);
537 		/* we only support string for now */
538 		if (num_src_targets && src_targets[0] == XA_STRING)
539 		    printf("src target ok\n");
540 	    } else
541 	    if (dnd_data.reason == DND_TOP_LEVEL_LEAVE) {
542 		printf("source sending a top level leave\n");
543 		cur_window = 0 ;
544 		/* no need to do anything */
545 	    } else
546 	    if (dnd_data.reason == DND_DROP_SITE_ENTER) {
547 		printf("receiver sending drop site enter\n");
548 		ChangeCursorValid(event.xclient.display, CurrentTime);
549 		do_drop = True ;
550 	    } else
551 	    if (dnd_data.reason == DND_DROP_SITE_LEAVE) {
552 		printf("receiver sending drop site leave\n");
553 		ChangeCursorInvalid(event.xclient.display, CurrentTime);
554 		do_drop = False ;
555 	    } else
556 	    if (dnd_data.reason == DND_OPERATION_CHANGED) {
557 		if (receiver) {
558 		    printf("receiver echoing operation changed\n");
559 		} else {
560 		    printf("source sending an operation changed\n");
561 		    /* need to echo */
562 		}
563 	    } else
564 	    if (dnd_data.reason == DND_DROP_START) {
565 		if (receiver) {
566 		    printf("receiver echoing drop start\n");
567 		} else {
568 		    printf("source sending a drop start\n");
569 
570 		    if (!in_drop_site) break ;
571 
572 		    /* need to echo and then request a convert */
573 		    dnd_data.reason = DND_DROP_START ;
574 		    dnd_data.time = CurrentTime ;
575 
576 		    DndFillClientMessage (event.xclient.display,
577 					  cur_window,
578 					  &cm, &dnd_data, 0);
579 
580 		    XSendEvent(event.xbutton.display,
581 			       cur_window, False, 0,
582 			       (XEvent *)&cm) ;
583 
584 
585 		    printf("XSendEvent DND_DROP_START %d\n",
586 			   cur_window);
587 
588 		    /* ask for a convertion - the selection name is
589 		     the same as the property on which the source
590 		     stored its target, weird, but that's the way it
591 		     was done in Motif... */
592 		    cv_targets[0] = XA_STRING ;
593 		    cv_targets[1] = XInternAtom(XtDisplay(top_level),
594 						"TEXT", False);
595 		    cv_targets[2] = XInternAtom(XtDisplay(top_level),
596 						"COMPOUND_TEXT", False);
597 		    XtGetSelectionValues(top_level, dnd_data.property,
598 					 cv_targets, XtNumber(cv_targets),
599 					 ReceiverConvert, closures,
600 					 CurrentTime);
601 
602 		}
603 		printf("drop action %d\n", dnd_data.completion);
604 	    }
605 	    break ;
606 	default:
607 	    break ;
608 	}
609 
610 	XtDispatchEvent(&event);
611     }
612 
613     exit(0);
614 }
615