1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2005 Novell, Inc.
23 // Jackson Harper (jackson@ximian.com)
32 using System.Collections;
33 using System.Runtime.Serialization;
34 using System.Runtime.InteropServices;
35 using System.Runtime.Serialization.Formatters.Binary;
37 namespace System.Windows.Forms {
39 internal class X11Dnd {
46 private enum DragState {
53 private interface IDataConverter {
54 void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent);
55 void SetData (X11Dnd dnd, object data, ref XEvent xevent);
58 private delegate void MimeConverter (IntPtr dsp,
59 DataObject data, ref XEvent xevent);
61 private class MimeHandler {
64 public IntPtr NonProtocol;
65 public IDataConverter Converter;
67 public MimeHandler (string name, IDataConverter converter)
70 Converter = converter;
73 public override string ToString ()
75 return "MimeHandler {" + Name + "}";
79 private MimeHandler [] MimeHandlers = {
80 // new MimeHandler ("WCF_DIB"),
81 // new MimeHandler ("image/gif", new MimeConverter (ImageConverter)),
82 // new MimeHandler ("text/rtf", new MimeConverter (RtfConverter)),
83 // new MimeHandler ("text/richtext", new MimeConverter (RtfConverter)),
85 new MimeHandler ("text/plain", new TextConverter ()),
86 new MimeHandler ("text/html", new HtmlConverter ()),
87 new MimeHandler ("text/uri-list", new UriListConverter ()),
88 new MimeHandler ("application/x-mono-serialized-object",
89 new SerializedObjectConverter ())
92 private class SerializedObjectConverter : IDataConverter {
94 public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
96 MemoryStream stream = dnd.GetData (ref xevent);
97 BinaryFormatter bf = new BinaryFormatter ();
99 if (stream.Length == 0)
103 object obj = bf.Deserialize (stream);
107 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
112 MemoryStream stream = new MemoryStream ();
113 BinaryFormatter bf = new BinaryFormatter ();
115 bf.Serialize (stream, data);
117 IntPtr buffer = Marshal.AllocHGlobal ((int) stream.Length);
120 for (int i = 0; i < stream.Length; i++) {
121 Marshal.WriteByte (buffer, i, (byte) stream.ReadByte ());
124 dnd.SetProperty (ref xevent, buffer, (int) stream.Length);
128 private class HtmlConverter : IDataConverter {
130 public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
132 string text = dnd.GetText (ref xevent, false);
135 data.SetData (DataFormats.Text, text);
136 data.SetData (DataFormats.UnicodeText, text);
139 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
143 string str = data as string;
148 if (xevent.SelectionRequestEvent.target == (int) Atom.XA_STRING) {
149 byte [] bytes = Encoding.ASCII.GetBytes (str);
150 buffer = Marshal.AllocHGlobal (bytes.Length);
152 for (int i = 0; i < len; i++)
153 Marshal.WriteByte (buffer, i, bytes [i]);
155 buffer = Marshal.StringToHGlobalAnsi (str);
157 while (Marshal.ReadByte (buffer, len) != 0)
161 dnd.SetProperty (ref xevent, buffer, len);
163 Marshal.FreeHGlobal (buffer);
167 private class TextConverter : IDataConverter {
169 public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
171 string text = dnd.GetText (ref xevent, true);
174 data.SetData (DataFormats.Text, text);
175 data.SetData (DataFormats.UnicodeText, text);
178 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
182 string str = data as string;
187 if (xevent.SelectionRequestEvent.target == (int) Atom.XA_STRING) {
188 byte [] bytes = Encoding.ASCII.GetBytes (str);
189 buffer = Marshal.AllocHGlobal (bytes.Length);
191 for (int i = 0; i < len; i++)
192 Marshal.WriteByte (buffer, i, bytes [i]);
194 buffer = Marshal.StringToHGlobalAnsi (str);
196 while (Marshal.ReadByte (buffer, len) != 0)
200 dnd.SetProperty (ref xevent, buffer, len);
202 Marshal.FreeHGlobal (buffer);
206 private class UriListConverter : IDataConverter {
208 public void GetData (X11Dnd dnd, DataObject data, ref XEvent xevent)
210 string text = dnd.GetText (ref xevent, false);
214 // TODO: Do this in a loop instead of just splitting
215 ArrayList uri_list = new ArrayList ();
216 string [] lines = text.Split (new char [] { '\r', '\n' });
217 foreach (string line in lines) {
218 // # is a comment line (see RFC 2483)
219 if (line.StartsWith ("#"))
222 Uri uri = new Uri (line);
223 uri_list.Add (uri.LocalPath);
227 string [] l = (string []) uri_list.ToArray (typeof (string));
230 data.SetData (DataFormats.FileDrop, l);
231 data.SetData ("FileName", l [0]);
232 data.SetData ("FileNameW", l [0]);
235 public void SetData (X11Dnd dnd, object data, ref XEvent xevent)
237 string [] uri_list = data as string [];
239 if (uri_list == null)
242 StringBuilder res = new StringBuilder ();
243 foreach (string uri_str in uri_list) {
244 Uri uri = new Uri (uri_str);
245 res.Append (uri.ToString ());
249 IntPtr buffer = Marshal.StringToHGlobalAnsi ((string) data);
251 while (Marshal.ReadByte (buffer, len) != 0)
254 dnd.SetProperty (ref xevent, buffer, len);
258 private class DragData {
259 public IntPtr Window;
260 public DragState State;
262 public IntPtr Action;
263 public IntPtr [] SupportedTypes;
265 public IntPtr LastWindow;
266 public IntPtr LastTopLevel;
268 public bool WillAccept;
272 State = DragState.None;
274 SupportedTypes = null;
279 private class DropData {
283 // This version seems to be the most common
284 private static readonly uint [] XdndVersion = new uint [] { 4 };
286 private IntPtr display;
287 private DragData drag_data;
289 private IntPtr XdndAware;
290 private IntPtr XdndSelection;
291 private IntPtr XdndEnter;
292 private IntPtr XdndLeave;
293 private IntPtr XdndPosition;
294 private IntPtr XdndDrop;
295 private IntPtr XdndFinished;
296 private IntPtr XdndStatus;
297 private IntPtr XdndTypeList;
298 private IntPtr XdndActionCopy;
299 private IntPtr XdndActionMove;
300 private IntPtr XdndActionLink;
301 private IntPtr XdndActionPrivate;
302 private IntPtr XdndActionList;
303 private IntPtr XdndActionDescription;
304 private IntPtr XdndActionAsk;
308 private int converts_pending;
309 private bool position_recieved;
310 private bool status_sent;
311 private IntPtr target;
312 private IntPtr source;
313 private IntPtr toplevel;
314 private DataObject data;
316 private IntPtr drag_action;
317 private Control control;
318 private int pos_x, pos_y;
319 private DragDropEffects allowed;
320 private DragEventArgs drag_event;
322 private Cursor CursorNo;
323 private Cursor CursorCopy;
324 private Cursor CursorMove;
325 private Cursor CursorLink;
326 private IntPtr CurrentCursorHandle;
328 public X11Dnd (IntPtr display)
330 this.display = display;
334 public void SetAllowDrop (Hwnd hwnd, bool allow)
336 if (hwnd.allow_drop == allow)
339 XChangeProperty (display, hwnd.whole_window, XdndAware,
340 (IntPtr) Atom.XA_ATOM, 32,
341 PropertyMode.Replace, XdndVersion, allow ? 1 : 0);
342 hwnd.allow_drop = allow;
345 public DragDropEffects StartDrag (IntPtr handle, object data,
346 DragDropEffects allowed_effects)
348 drag_data = new DragData ();
349 drag_data.Window = handle;
350 drag_data.State = DragState.Beginning;
352 drag_data.Data = data;
353 drag_data.SupportedTypes = DetermineSupportedTypes (data);
355 drag_data.Action = ActionFromEffect (allowed_effects);
357 if (CursorNo == null) {
358 // Make sure the cursors are created
359 CursorNo = new Cursor (typeof (X11Dnd), "DnDNo.cur");
360 CursorCopy = new Cursor (typeof (X11Dnd), "DnDCopy.cur");
361 CursorMove = new Cursor (typeof (X11Dnd), "DnDMove.cur");
362 CursorLink = new Cursor (typeof (X11Dnd), "DnDLink.cur");
365 drag_data.LastTopLevel = IntPtr.Zero;
366 return DragDropEffects.Copy;
369 public void HandleButtonRelease (ref XEvent xevent)
372 if (drag_data == null)
375 if (drag_data.State == DragState.Beginning) {
376 state = State.Accepting;
377 } else if (drag_data.State != DragState.None) {
379 if (drag_data.WillAccept) {
380 SendDrop (drag_data.LastTopLevel, xevent.AnyEvent.window,
381 xevent.ButtonEvent.time);
384 XplatUIX11.XUngrabPointer (display, 0);
385 drag_data.State = DragState.None;
386 // WE can't reset the drag data yet as it is still
387 // most likely going to be used by the SelectionRequest
392 public void HandleMotionNotify (ref XEvent xevent)
394 if (drag_data == null)
397 if (drag_data.State == DragState.Beginning) {
400 drag_data.State = DragState.Dragging;
402 suc = XplatUIX11.XSetSelectionOwner (display, (int) XdndSelection,
404 xevent.ButtonEvent.time);
407 Console.Error.WriteLine ("Could not take ownership of XdndSelection aborting drag.");
412 suc = XGrabPointer (display, xevent.AnyEvent.window,
414 EventMask.ButtonMotionMask |
415 EventMask.PointerMotionMask |
416 EventMask.ButtonPressMask |
417 EventMask.ButtonReleaseMask,
418 GrabMode.GrabModeAsync,
419 GrabMode.GrabModeAsync,
420 IntPtr.Zero, IntPtr.Zero/*CursorCopy.Handle*/, 0);
423 Console.Error.WriteLine ("Could not grab pointer aborting drag.");
428 drag_data.State = DragState.Dragging;
429 } else if (drag_data.State != DragState.None) {
430 XAllowEvents (display, 1 /* SyncPointer */, IntPtr.Zero);
432 bool dnd_aware = false;
433 IntPtr toplevel = IntPtr.Zero;
434 IntPtr window = XplatUIX11.RootWindowHandle;
440 while (XQueryPointer (display,
443 out x_temp, out y_temp,
444 out xevent.MotionEvent.x,
445 out xevent.MotionEvent.y,
449 dnd_aware = IsWindowDndAware (window);
452 xevent.MotionEvent.x_root = x_temp;
453 xevent.MotionEvent.y_root = y_temp;
457 if (child == IntPtr.Zero)
463 if (window != drag_data.LastWindow && drag_data.State == DragState.Entered) {
464 drag_data.State = DragState.Dragging;
466 // TODO: Send a Leave if this is an MWF window
468 if (toplevel != drag_data.LastTopLevel)
469 SendLeave (drag_data.LastTopLevel, xevent.MotionEvent.window);
472 drag_data.State = DragState.Entered;
473 if (toplevel != drag_data.LastTopLevel) {
474 // Entering a new toplevel window
475 SendEnter (toplevel, drag_data.Window, drag_data.SupportedTypes);
477 // Already in a toplevel window, so send a position
478 SendPosition (toplevel, drag_data.Window,
480 xevent.MotionEvent.x_root,
481 xevent.MotionEvent.y_root,
482 xevent.MotionEvent.time);
485 drag_data.LastTopLevel = toplevel;
486 drag_data.LastWindow = window;
491 private string GetText (IntPtr handle) {
492 string text = String.Empty;
495 textptr = IntPtr.Zero;
497 XFetchName (display, handle, ref textptr);
498 if (textptr != IntPtr.Zero) {
499 text = Marshal.PtrToStringAnsi(textptr);
507 // return true if the event is handled here
508 public bool HandleClientMessage (ref XEvent xevent)
510 // most common so we check it first
511 if (xevent.ClientMessageEvent.message_type == XdndPosition)
512 return Accepting_HandlePositionEvent (ref xevent);
513 if (xevent.ClientMessageEvent.message_type == XdndEnter)
514 return Accepting_HandleEnterEvent (ref xevent);
515 if (xevent.ClientMessageEvent.message_type == XdndDrop)
516 return Accepting_HandleDropEvent (ref xevent);
517 if (xevent.ClientMessageEvent.message_type == XdndLeave)
518 return Accepting_HandleLeaveEvent (ref xevent);
519 if (xevent.ClientMessageEvent.message_type == XdndStatus)
520 return HandleStatusEvent (ref xevent);
525 public bool HandleSelectionNotifyEvent (ref XEvent xevent)
527 if (source != XGetSelectionOwner (display, XdndSelection))
530 MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionEvent.target);
534 data = new DataObject ();
536 handler.Converter.GetData (this, data, ref xevent);
539 if (converts_pending <= 0 && position_recieved)
544 public bool HandleSelectionRequestEvent (ref XEvent xevent)
546 if (xevent.SelectionRequestEvent.selection != (int) XdndSelection)
549 MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionRequestEvent.target);
553 handler.Converter.SetData (this, drag_data.Data, ref xevent);
558 private void SetProperty (ref XEvent xevent, IntPtr data, int length)
560 XEvent sel = new XEvent();
561 sel.SelectionEvent.type = XEventName.SelectionNotify;
562 sel.SelectionEvent.send_event = true;
563 sel.SelectionEvent.display = display;
564 sel.SelectionEvent.selection = xevent.SelectionRequestEvent.selection;
565 sel.SelectionEvent.target = xevent.SelectionRequestEvent.target;
566 sel.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor;
567 sel.SelectionEvent.time = xevent.SelectionRequestEvent.time;
568 sel.SelectionEvent.property = 0;
570 XplatUIX11.XChangeProperty (display, xevent.SelectionRequestEvent.requestor,
571 xevent.SelectionRequestEvent.property,
572 xevent.SelectionRequestEvent.target,
573 8, PropertyMode.Replace, data, length);
574 sel.SelectionEvent.property = xevent.SelectionRequestEvent.property;
576 XSendEvent (display, xevent.SelectionRequestEvent.requestor, false,
577 EventMask.NoEventMask, ref sel);
581 private void Reset ()
587 private void ResetSourceData ()
589 converts_pending = 0;
593 private void ResetTargetData ()
595 position_recieved = false;
599 private bool Accepting_HandleEnterEvent (ref XEvent xevent)
603 source = xevent.ClientMessageEvent.ptr1;
604 toplevel = xevent.AnyEvent.window;
605 target = IntPtr.Zero;
607 ConvertData (ref xevent);
612 private bool Accepting_HandlePositionEvent (ref XEvent xevent)
614 int x = (int) xevent.ClientMessageEvent.ptr3 >> 16;
615 int y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF;
617 allowed = EffectFromAction (xevent.ClientMessageEvent.ptr5);
619 IntPtr parent, child, new_child;
620 parent = XplatUIX11.XRootWindow (display, 0);
624 new_child = IntPtr.Zero;
626 if (!XplatUIX11.XTranslateCoordinates (display,
628 out xd, out yd, out new_child))
630 if (new_child == IntPtr.Zero)
635 if (target != child) {
636 // We have moved into a new control
637 // or into a control for the first time
641 Hwnd hwnd = Hwnd.ObjectFromHandle (target);
642 Control c = Control.FromHandle (hwnd.client_window);
652 position_recieved = true;
654 if (converts_pending > 0)
660 control.DndOver (drag_event);
666 private void Finish ()
668 if (control != null) {
669 if (drag_event == null) {
671 data = new DataObject ();
672 drag_event = new DragEventArgs (data,
674 allowed, DragDropEffects.None);
676 control.DndLeave (drag_event);
681 private bool Accepting_HandleDropEvent (ref XEvent xevent)
683 if (control != null && drag_event != null) {
684 drag_event = new DragEventArgs (data,
686 allowed, DragDropEffects.None);
687 control.DndDrop (drag_event);
693 private bool Accepting_HandleLeaveEvent (ref XEvent xevent)
695 if (control != null && drag_event != null)
696 control.DndLeave (drag_event);
701 private bool HandleStatusEvent (ref XEvent xevent)
703 if (drag_data != null && drag_data.State == DragState.Entered) {
704 drag_data.WillAccept = ((int) xevent.ClientMessageEvent.ptr2 & 0x1) != 0;
705 Cursor cursor = CursorNo;
706 if (drag_data.WillAccept) {
707 // Same order as on MS
708 IntPtr action = (int) xevent.ClientMessageEvent.ptr5;
709 if ((action == XdndActionCopy)
711 else if (action == XdndActionLink)
713 else if (action == XdndActionMove)
718 // TODO: Try not to set the cursor so much
719 //if (cursor.Handle != CurrentCursorHandle) {
720 XChangeActivePointerGrab (display,
721 EventMask.ButtonMotionMask |
722 EventMask.PointerMotionMask |
723 EventMask.ButtonPressMask |
724 EventMask.ButtonReleaseMask,
725 cursor.Handle, IntPtr.Zero);
726 CurrentCursorHandle = cursor.Handle;
732 private DragDropEffects EffectFromAction (IntPtr action)
734 DragDropEffects allowed = DragDropEffects.None;
736 if (action == XdndActionCopy)
737 allowed = DragDropEffects.Copy;
738 else if (action == XdndActionMove)
739 allowed |= DragDropEffects.Move;
740 if (action == XdndActionLink)
741 allowed |= DragDropEffects.Link;
745 private IntPtr ActionFromEffect (DragDropEffects effect)
747 IntPtr action = IntPtr.Zero;
749 // We can't OR together actions on XDND so sadly the primary
750 // is the only one shown here
751 if ((effect & DragDropEffects.Copy) != 0)
752 action = XdndActionCopy;
753 else if ((effect & DragDropEffects.Move) != 0)
754 action = XdndActionMove;
755 else if ((effect & DragDropEffects.Link) != 0)
756 action = XdndActionLink;
760 private bool ConvertData (ref XEvent xevent)
764 XplatUIX11.XGetSelectionOwner (display, (int) XdndSelection);
766 foreach (IntPtr atom in SourceSupportedList (ref xevent)) {
767 MimeHandler handler = FindHandler (atom);
770 XConvertSelection (display, XdndSelection, handler.Type,
771 handler.NonProtocol, toplevel, 0 /* CurrentTime */);
778 private MimeHandler FindHandler (IntPtr atom)
780 if (atom == IntPtr.Zero)
782 foreach (MimeHandler handler in MimeHandlers) {
783 if (handler.Type == atom)
789 private MimeHandler FindHandler (string name)
791 foreach (MimeHandler handler in MimeHandlers) {
792 if (handler.Name == name)
798 private void SendStatus ()
800 XEvent xevent = new XEvent ();
802 xevent.AnyEvent.type = XEventName.ClientMessage;
803 xevent.AnyEvent.display = display;
804 xevent.ClientMessageEvent.window = source;
805 xevent.ClientMessageEvent.message_type = XdndStatus;
806 xevent.ClientMessageEvent.format = 32;
807 xevent.ClientMessageEvent.ptr1 = toplevel;
808 if (drag_event.Effect != DragDropEffects.None)
809 xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
811 xevent.ClientMessageEvent.ptr5 = ActionFromEffect (drag_event.Effect);
812 XSendEvent (display, source, false, 0, ref xevent);
815 private void SendEnterStatus ()
817 drag_event = new DragEventArgs (data, 0, pos_x, pos_y,
818 allowed, DragDropEffects.None);
819 control.DndEnter (drag_event);
821 XEvent xevent = new XEvent ();
823 xevent.AnyEvent.type = XEventName.ClientMessage;
824 xevent.AnyEvent.display = display;
825 xevent.ClientMessageEvent.window = source;
826 xevent.ClientMessageEvent.message_type = XdndStatus;
827 xevent.ClientMessageEvent.format = 32;
828 xevent.ClientMessageEvent.ptr1 = toplevel;
829 if (drag_event.Effect != DragDropEffects.None)
830 xevent.ClientMessageEvent.ptr2 = (IntPtr) 1;
832 xevent.ClientMessageEvent.ptr5 = ActionFromEffect (drag_event.Effect);
833 XSendEvent (display, source, false, 0, ref xevent);
838 private void SendEnter (IntPtr handle, IntPtr from, IntPtr [] supported)
840 XEvent xevent = new XEvent ();
842 xevent.AnyEvent.type = XEventName.ClientMessage;
843 xevent.AnyEvent.display = display;
844 xevent.ClientMessageEvent.window = handle;
845 xevent.ClientMessageEvent.message_type = XdndEnter;
846 xevent.ClientMessageEvent.format = 32;
847 xevent.ClientMessageEvent.ptr1 = from;
849 // (int) xevent.ClientMessageEvent.ptr2 & 0x1)
851 // xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2;
852 // (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
853 xevent.ClientMessageEvent.ptr2 = (IntPtr) (XdndVersion [0] << 24);
855 if (supported.Length > 0)
856 xevent.ClientMessageEvent.ptr3 = supported [0];
857 if (supported.Length > 1)
858 xevent.ClientMessageEvent.ptr4 = supported [1];
859 if (supported.Length > 2)
860 xevent.ClientMessageEvent.ptr5 = supported [2];
862 XSendEvent (display, handle, false, 0, ref xevent);
865 private void SendDrop (IntPtr handle, IntPtr from, IntPtr time)
867 XEvent xevent = new XEvent ();
869 xevent.AnyEvent.type = XEventName.ClientMessage;
870 xevent.AnyEvent.display = display;
871 xevent.ClientMessageEvent.window = handle;
872 xevent.ClientMessageEvent.message_type = XdndDrop;
873 xevent.ClientMessageEvent.format = 32;
874 xevent.ClientMessageEvent.ptr1 = from;
875 xevent.ClientMessageEvent.ptr3 = time;
877 XSendEvent (display, handle, false, 0, ref xevent);
880 private void SendPosition (IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time)
882 XEvent xevent = new XEvent ();
884 xevent.AnyEvent.type = XEventName.ClientMessage;
885 xevent.AnyEvent.display = display;
886 xevent.ClientMessageEvent.window = handle;
887 xevent.ClientMessageEvent.message_type = XdndPosition;
888 xevent.ClientMessageEvent.format = 32;
889 xevent.ClientMessageEvent.ptr1 = from;
890 xevent.ClientMessageEvent.ptr3 = (IntPtr) ((x << 16) | (y & 0xFFFF));
891 xevent.ClientMessageEvent.ptr4 = time;
892 xevent.ClientMessageEvent.ptr5 = action;
894 XSendEvent (display, handle, false, 0, ref xevent);
897 private void SendLeave (IntPtr handle, IntPtr from)
899 XEvent xevent = new XEvent ();
901 xevent.AnyEvent.type = XEventName.ClientMessage;
902 xevent.AnyEvent.display = display;
903 xevent.ClientMessageEvent.window = handle;
904 xevent.ClientMessageEvent.message_type = XdndLeave;
905 xevent.ClientMessageEvent.format = 32;
906 xevent.ClientMessageEvent.ptr1 = from;
908 XSendEvent (display, handle, false, 0, ref xevent);
911 private void SendFinished ()
913 XEvent xevent = new XEvent ();
915 xevent.AnyEvent.type = XEventName.ClientMessage;
916 xevent.AnyEvent.display = display;
917 xevent.ClientMessageEvent.window = source;
918 xevent.ClientMessageEvent.message_type = XdndFinished;
919 xevent.ClientMessageEvent.format = 32;
920 xevent.ClientMessageEvent.ptr1 = toplevel;
922 XSendEvent (display, source, false, 0, ref xevent);
925 // There is a somewhat decent amount of overhead
926 // involved in setting up dnd so we do it lazily
927 // as a lot of applications do not even use it.
930 XdndAware = XInternAtom (display, "XdndAware", false);
931 XdndEnter = XInternAtom (display, "XdndEnter", false);
932 XdndLeave = XInternAtom (display, "XdndLeave", false);
933 XdndPosition = XInternAtom (display, "XdndPosition", false);
934 XdndStatus = XInternAtom (display, "XdndStatus", false);
935 XdndDrop = XInternAtom (display, "XdndDrop", false);
936 XdndSelection = XInternAtom (display, "XdndSelection", false);
937 XdndFinished = XInternAtom (display, "XdndFinished", false);
938 XdndTypeList = XInternAtom (display, "XdndTypeList", false);
939 XdndActionCopy = XInternAtom (display, "XdndActionCopy", false);
940 XdndActionMove = XInternAtom (display, "XdndActionMove", false);
941 XdndActionLink = XInternAtom (display, "XdndActionLink", false);
942 XdndActionPrivate = XInternAtom (display, "XdndActionPrivate", false);
943 XdndActionList = XInternAtom (display, "XdndActionList", false);
944 XdndActionDescription = XInternAtom (display, "XdndActionDescription", false);
945 XdndActionAsk = XInternAtom (display, "XdndActionAsk", false);
947 foreach (MimeHandler handler in MimeHandlers) {
948 handler.Type = XInternAtom (display, handler.Name, false);
949 handler.NonProtocol = XInternAtom (display,
950 String.Concat ("MWFNonP+", handler.Name), false);
955 private IntPtr [] SourceSupportedList (ref XEvent xevent)
960 if (((int) xevent.ClientMessageEvent.ptr2 & 0x1) == 0) {
961 res = new IntPtr [3];
962 res [0] = xevent.ClientMessageEvent.ptr3;
963 res [1] = xevent.ClientMessageEvent.ptr4;
964 res [2] = xevent.ClientMessageEvent.ptr5;
967 int format, count, remaining;
968 IntPtr data = IntPtr.Zero;
970 XGetWindowProperty (display, source, XdndTypeList,
971 0, 32, false, (IntPtr) Atom.XA_ATOM,
972 out type, out format, out count,
973 out remaining, out data);
975 res = new IntPtr [count];
976 for (int i = 0; i < count; i++) {
977 res [i] = (IntPtr) Marshal.ReadInt32 (data, i *
978 Marshal.SizeOf (typeof (int)));
987 private string GetText (ref XEvent xevent, bool unicode)
993 StringBuilder builder = new StringBuilder ();
997 IntPtr data = IntPtr.Zero;
999 if (0 != XGetWindowProperty (display,
1000 xevent.AnyEvent.window,
1001 (IntPtr) xevent.SelectionEvent.property,
1003 (IntPtr) Atom.AnyPropertyType, out actual_type,
1004 out actual_fmt, out nitems, out bytes_after,
1011 builder.Append (Marshal.PtrToStringUni (data));
1013 builder.Append (Marshal.PtrToStringAnsi (data));
1017 } while (bytes_after > 0);
1020 return builder.ToString ();
1023 private MemoryStream GetData (ref XEvent xevent)
1029 MemoryStream res = new MemoryStream ();
1033 IntPtr data = IntPtr.Zero;
1035 if (0 != XGetWindowProperty (display,
1036 xevent.AnyEvent.window,
1037 (IntPtr) xevent.SelectionEvent.property,
1039 (IntPtr) Atom.AnyPropertyType, out actual_type,
1040 out actual_fmt, out nitems, out bytes_after,
1046 for (int i = 0; i < nitems; i++)
1047 res.WriteByte (Marshal.ReadByte (data, i));
1051 } while (bytes_after > 0);
1055 private Control MwfWindow (IntPtr window)
1057 Hwnd hwnd = Hwnd.ObjectFromHandle (window);
1061 Control res = Control.FromHandle (hwnd.client_window);
1065 private bool IsWindowDndAware (IntPtr handle)
1068 // Check the version number, we need greater than 3
1070 int format, count, remaining;
1071 IntPtr data = IntPtr.Zero;
1073 XGetWindowProperty (display, handle, XdndAware, 0, 0x8000000, false,
1074 (IntPtr) Atom.XA_ATOM, out actual, out format,
1075 out count, out remaining, out data);
1077 if (actual != (IntPtr) Atom.XA_ATOM || format != 32 ||
1078 count == 0 || data == IntPtr.Zero) {
1079 if (data != IntPtr.Zero)
1084 int version = Marshal.ReadInt32 (data, 0);
1087 Console.Error.WriteLine ("XDND Version too old (" + version + ").");
1092 // First type is actually the XDND version
1095 for (int i = 1; i < count; i++) {
1096 IntPtr type = (IntPtr) Marshal.ReadInt32 (data, i *
1097 Marshal.SizeOf (typeof (int)));
1098 for (int j = 0; j < drag_data.SupportedTypes.Length; j++) {
1099 if (drag_data.SupportedTypes [j] == type) {
1111 private IntPtr [] DetermineSupportedTypes (object data)
1113 ArrayList res = new ArrayList ();
1115 if (data is string) {
1116 MimeHandler handler = FindHandler ("text/plain");
1117 if (handler != null)
1118 res.Add (handler.Type);
1119 }/* else if (data is Bitmap)
1124 if (data is IDataObject) {
1129 if (data is ISerializable) {
1130 MimeHandler handler = FindHandler ("application/x-mono-serialized-object");
1131 if (handler != null)
1132 res.Add (handler.Type);
1135 return (IntPtr []) res.ToArray (typeof (IntPtr));
1138 [DllImport ("libX11")]
1139 private extern static string XGetAtomName (IntPtr display, IntPtr atom);
1141 [DllImport ("libX11")]
1142 private extern static IntPtr XInternAtom (IntPtr display, string atom_name, bool only_if_exists);
1144 [DllImport ("libX11")]
1145 private extern static int XChangeProperty (IntPtr display, IntPtr window, IntPtr property,
1146 IntPtr format, int type, PropertyMode mode, uint [] atoms, int nelements);
1148 [DllImport ("libX11")]
1149 private extern static int XGetWindowProperty (IntPtr display, IntPtr window,
1150 IntPtr atom, int long_offset, int long_length, bool delete,
1151 IntPtr req_type, out IntPtr actual_type, out int actual_format,
1152 out int nitems, out int bytes_after, out IntPtr prop);
1154 [DllImport ("libX11")]
1155 internal extern static int XSendEvent (IntPtr display, IntPtr window,
1156 bool propagate, EventMask event_mask, ref XEvent send_event);
1158 [DllImport ("libX11")]
1159 internal extern static int XConvertSelection (IntPtr display, IntPtr selection,
1160 IntPtr target, IntPtr property, IntPtr requestor, int time);
1162 [DllImport ("libX11")]
1163 internal extern static IntPtr XGetSelectionOwner (IntPtr display, IntPtr selection);
1165 [DllImport ("libX11")]
1166 internal extern static int XGrabPointer (IntPtr display, IntPtr window,
1167 bool owner_events, EventMask event_mask, GrabMode pointer_mode,
1168 GrabMode keyboard_mode, IntPtr confine_to, IntPtr cursor, uint timestamp);
1170 [DllImport ("libX11")]
1171 internal extern static bool XQueryPointer (IntPtr display, IntPtr window, out IntPtr root,
1172 out IntPtr child, out int root_x, out int root_y, out int win_x,
1173 out int win_y, out int keys_buttons);
1175 [DllImport ("libX11")]
1176 internal extern static int XAllowEvents (IntPtr display, int event_mode, IntPtr time);
1178 [DllImport ("libX11")]
1179 internal extern static int XFree(IntPtr data);
1181 [DllImport ("libX11")]
1182 internal extern static int XFetchName (IntPtr display, IntPtr window, ref IntPtr window_name);
1183 [DllImport ("libX11")]
1184 internal extern static int XChangeActivePointerGrab (IntPtr display, EventMask event_mask, IntPtr cursor, IntPtr time);