How to simulate dragging? - Xwindows

This is a discussion on How to simulate dragging? - Xwindows ; I am trying to write a program that will allow the user to drag a window from any area in that window besides just the title bar. I have not been successful and am experiencing weird behaviour that I do ...

+ Reply to Thread
Results 1 to 8 of 8

Thread: How to simulate dragging?

  1. How to simulate dragging?

    I am trying to write a program that will allow the user to drag a
    window from any area in that window besides just the title bar.

    I have not been successful and am experiencing weird behaviour that I
    do not understand. Despite how I move the mouse, the window insists
    on travelling downward toward the bottom of the screen.

    Here is the code relevant to the drag implementation:

    ....

    ::XSelectInput(g_pDisplay, g_window, ExposureMask | ButtonPressMask |
    Button1MotionMask | StructureNotifyMask);

    ....

    XEvent event;
    ::XNextEvent(g_pDisplay, &event);

    static int nMouseX;
    static int nMouseY;

    switch(event.type){
    case ButtonPress:
    if(Button1 == event.xbutton.button){
    nMouseX = event.xbutton.x_root;
    nMouseY = event.xbutton.y_root;
    }
    break;

    case MotionNotify:
    {
    int nDragX = event.xmotion.x_root;
    int nDragY = event.xmotion.y_root;

    int nXChange = nDragX - nMouseX;
    int nYChange = nDragY - nMouseY;

    nMouseX = nDragX;
    nMouseY = nDragY;

    XWindowAttributes attributes;
    ::XGetWindowAttributes(g_pDisplay, g_window, &attributes);

    Window root = RootWindow(g_pDisplay, g_nScreen);
    int x, y;
    Window child;
    ::XTranslateCoordinates(g_pDisplay, g_window, root, attributes.x,
    attributes.y, &x, &y, &child);

    using std::clog;
    clog << "Moving window from " << x << ", " << y << " to ";
    x += nXChange;
    y += nYChange;

    clog << x << ", " << y << ".\n";

    ::XMoveWindow(g_pDisplay, g_window, x , y);

    ::XGetWindowAttributes(g_pDisplay, g_window, &attributes);
    ::XTranslateCoordinates(g_pDisplay, g_window, root,
    attributes.x,
    attributes.y, &x, &y, &child);
    clog << "Window moved to " << x << ", " << y << ".\n";

    break;
    }

    This is the kind of output I get:

    Mouse moved from 347, 156 to 348, 156.
    Moving window from 9, 60 to 10, 60.
    Window moved to 16, 100.
    Mouse moved from 348, 156 to 349, 157.
    Moving window from 16, 100 to 17, 101.
    Window moved to 23, 141.
    Mouse moved from 349, 157 to 350, 157.
    Moving window from 23, 141 to 24, 141.
    Window moved to 30, 181.
    Mouse moved from 350, 157 to 351, 157.
    Moving window from 30, 181 to 31, 181.
    Window moved to 37, 221.
    Mouse moved from 351, 157 to 352, 158.
    Moving window from 37, 221 to 38, 222.
    Window moved to 44, 262.

    Why is this dragging code behaving in this manner?

  2. Re: How to simulate dragging?

    I'll post this a second time since the first try did not propagate --

    Tron Thomas wrote:
    >I am trying to write a program that will allow the user to drag a
    >window from any area in that window besides just the title bar.
    >
    >I have not been successful and am experiencing weird behaviour that I
    >do not understand. Despite how I move the mouse, the window insists
    >on travelling downward toward the bottom of the screen.
    >
    >Here is the code relevant to the drag implementation:
    >
    >...
    >
    >::XSelectInput(g_pDisplay, g_window, ExposureMask | ButtonPressMask |
    > Button1MotionMask | StructureNotifyMask);
    >
    >...
    >
    >XEvent event;
    >::XNextEvent(g_pDisplay, &event);
    >
    >static int nMouseX;
    >static int nMouseY;
    >
    >switch(event.type){
    > case ButtonPress:
    > if(Button1 == event.xbutton.button){
    > nMouseX = event.xbutton.x_root;
    > nMouseY = event.xbutton.y_root;
    > }
    > break;
    >
    > case MotionNotify:
    > {
    > int nDragX = event.xmotion.x_root;
    > int nDragY = event.xmotion.y_root;
    >
    > int nXChange = nDragX - nMouseX;
    > int nYChange = nDragY - nMouseY;
    >
    > nMouseX = nDragX;
    > nMouseY = nDragY;
    >
    > XWindowAttributes attributes;
    > ::XGetWindowAttributes(g_pDisplay, g_window, &attributes);
    >
    > Window root = RootWindow(g_pDisplay, g_nScreen);
    > int x, y;
    > Window child;
    > ::XTranslateCoordinates(g_pDisplay, g_window, root, attributes.x,
    > attributes.y, &x, &y, &child);


    I guess this should be something like

    ::XTranslateCoordinates(g_pDisplay, g_window, root,
    -attributes.border_width,
    -attributes.border_width, &x, &y, &child);

    >
    > using std::clog;
    > clog << "Moving window from " << x << ", " << y << " to ";
    > x += nXChange;
    > y += nYChange;
    >
    > clog << x << ", " << y << ".\n";
    >
    > ::XMoveWindow(g_pDisplay, g_window, x , y);

    [snip]


  3. Re: How to simulate dragging?

    The attribute.border_width value is zero so, it doesn't look like
    incorporating that into the calculation would be helpful.

    I'm pretty sure it has to do with the items the window manager places
    around the window when it displays on the screen. I'm just not sure
    how to deal with this.

    Also I'm getting inconsistent results when trying to determine where
    the window is placed on the screen.

    I wrote a program that places a window on the screen using this code:

    Window root = DefaultRootWindow(g_pDisplay);
    XWindowAttributes attributes;
    ::XGetWindowAttributes(g_pDisplay, root, &attributes);
    unsigned int nScreenWidth = attributes.width;
    unsigned int nScreenHeight = attributes.height;

    using std::clog;
    clog << "The screen is " << nScreenWidth << " X " << nScreenHeight <<
    ".\n";

    ::XGetWindowAttributes(g_pDisplay, g_window, &attributes);

    clog << "The window is " << attributes.width << " X " <<
    attributes.height << ".\n";

    int x = (nScreenWidth - attributes.width) / 2;
    int y = (nScreenHeight - attributes.height) / 2;

    clog << "Moving window to " << x << ", " << y << ".\n";
    ::XMoveWindow(g_pDisplay, g_window, x, y);

    ::XGetWindowAttributes(g_pDisplay, g_window, &attributes);

    Window child;
    ::XTranslateCoordinates(g_pDisplay, g_window, root, attributes.x,
    attributes.y, &x, &y, &child);

    clog << "Window is now at " << x << ", " << y << ".\n";

    Sometimes I get results like:

    The screen is 1280 X 1024.
    The window is 640 X 480.
    Moving window to 320, 272.
    Window is now at 326, 312.

    Which seems quite reasonable. However, other times I get this result:

    The screen is 1280 X 1024.
    The window is 640 X 480.
    Moving window to 320, 272.
    Window is now at 6, 40.

    This is clearly wrong and may cause problems with my drag
    implementation.

    kbr@pangea.ca (Kip Rugger) wrote in message news:...
    > I'll post this a second time since the first try did not propagate --
    > I guess this should be something like
    >
    > ::XTranslateCoordinates(g_pDisplay, g_window, root,
    > -attributes.border_width,
    > -attributes.border_width, &x, &y, &child);
    >


  4. Re: How to simulate dragging?

    Tron Thomas wrote:
    >The attribute.border_width value is zero so, it doesn't look like
    >incorporating that into the calculation would be helpful.
    >
    >I'm pretty sure it has to do with the items the window manager places
    >around the window when it displays on the screen. I'm just not sure
    >how to deal with this.
    >
    >Also I'm getting inconsistent results when trying to determine where
    >the window is placed on the screen.
    >
    >I wrote a program that places a window on the screen using this code:
    >
    >Window root = DefaultRootWindow(g_pDisplay);
    >XWindowAttributes attributes;
    >::XGetWindowAttributes(g_pDisplay, root, &attributes);
    >unsigned int nScreenWidth = attributes.width;
    >unsigned int nScreenHeight = attributes.height;
    >
    >using std::clog;
    >clog << "The screen is " << nScreenWidth << " X " << nScreenHeight <<
    > ".\n";
    >
    >::XGetWindowAttributes(g_pDisplay, g_window, &attributes);
    >
    >clog << "The window is " << attributes.width << " X " <<
    > attributes.height << ".\n";
    >
    >int x = (nScreenWidth - attributes.width) / 2;
    >int y = (nScreenHeight - attributes.height) / 2;
    >
    >clog << "Moving window to " << x << ", " << y << ".\n";
    >::XMoveWindow(g_pDisplay, g_window, x, y);
    >
    >::XGetWindowAttributes(g_pDisplay, g_window, &attributes);
    >
    >Window child;
    >::XTranslateCoordinates(g_pDisplay, g_window, root, attributes.x,
    > attributes.y, &x, &y, &child);
    >
    >clog << "Window is now at " << x << ", " << y << ".\n";
    >
    >Sometimes I get results like:
    >
    >The screen is 1280 X 1024.
    >The window is 640 X 480.
    >Moving window to 320, 272.
    >Window is now at 326, 312.
    >
    >Which seems quite reasonable. However, other times I get this result:
    >
    >The screen is 1280 X 1024.
    >The window is 640 X 480.
    >Moving window to 320, 272.
    >Window is now at 6, 40.
    >
    >This is clearly wrong and may cause problems with my drag
    >implementation.


    I think you missed my main point; the border width is just a find detail.

    Your original code was doing the following:

    - GetWinAttributes for the (x,y) position of the window which gives
    the coordinates in the window manager's frame window
    - TranslateCoordinates treating that (x,y) pair as _client window_
    coordinates
    - MoveWindow on the result

    We have three coordinate systems here, root (R), frame (F), and client (C).
    GetWinAttr returns a result in F; you then use Translate to convert from
    C to R, but you are converting a value that belongs to F. The F coords
    passed in likely are something like (0, titlebar_height), so the R result
    from Trans is too large in the y-coord; your window slides off the bottom
    of the screen.

    The correct procedure, then, is to pass C coords to Translate. This is
    usually (0,0) -- "tell me where the upper-left point of my window is in
    R coords".

    As usual, the situation is a little more complicated: there is the issue
    of `window gravity', which is an attribute you can set and change on any
    of your windows.

    When you use MoveWindow, you pass in an (x,y) value, but that is not
    necessarily the location at which the upper-left corner of the window
    will be placed. What actually happens is that a reference point on the
    window -- defined by window gravity -- is placed at (x,y). So if a
    window has SE win gravity, the (x,y) will specify the position of the
    bottom-right corner, borders included.

    The default win gravity is NorthWest, meaning that the upper-left corner
    of the window -- borders included -- is placed at (x,y).

    Returning to your example, you probably have NW gravity and border_width
    of zero, so translating (0,0) in C coordinates to R should work. You
    then supply the mouse-move delta, and then MoveWindow.

    I suggested translating (-bw,-bw) in C to R which would be required if
    bw were not zero. It's easy to do, and good practice. Remember that
    the window manager is free to change bw on your top-level windows.

    The most general form would be to examine the win gravity, and pass the
    correct C value to Translate. For example, in SE gravity you would pass
    (width+2*bw, height+2*bw). This type of code would be required in
    general-purpose software, such as libraries or window managers. (And
    not all window managers get this one right; as always, check what twm
    does before blaming your code since twm does it right.)


  5. Re: How to simulate dragging?

    XGetWindowAttributes always returns 0 for both the x and y fields of
    the XWindowAttributes structure. This implies that it is not
    returning the points relative to the frame which should, as you
    mentioned below, something like (frame_border_width, titlebar_height).

    So, it looks like I have been doing a client to root transformation
    when I called XTranslateCoordinates, and not a frame to root
    transformation. It also looks like when I call XMoveWindow the window
    gets moved to a position that is not where I told it to move to
    because the window manager is getting in the way and adjusting the
    window to a different location.

    I need a way to predict what the window manager is doing so I can
    compensate and make sure the window gets places where it should be.

    kbr@pangea.ca (Kip Rugger) wrote in message news:...
    > I think you missed my main point; the border width is just a find detail.
    >
    > Your original code was doing the following:
    >
    > - GetWinAttributes for the (x,y) position of the window which gives
    > the coordinates in the window manager's frame window
    > - TranslateCoordinates treating that (x,y) pair as _client window_
    > coordinates
    > - MoveWindow on the result
    >
    > We have three coordinate systems here, root (R), frame (F), and client (C).
    > GetWinAttr returns a result in F; you then use Translate to convert from
    > C to R, but you are converting a value that belongs to F. The F coords
    > passed in likely are something like (0, titlebar_height), so the R result
    > from Trans is too large in the y-coord; your window slides off the bottom
    > of the screen.
    >
    > The correct procedure, then, is to pass C coords to Translate. This is
    > usually (0,0) -- "tell me where the upper-left point of my window is in
    > R coords".
    >
    > As usual, the situation is a little more complicated: there is the issue
    > of `window gravity', which is an attribute you can set and change on any
    > of your windows.
    >
    > When you use MoveWindow, you pass in an (x,y) value, but that is not
    > necessarily the location at which the upper-left corner of the window
    > will be placed. What actually happens is that a reference point on the
    > window -- defined by window gravity -- is placed at (x,y). So if a
    > window has SE win gravity, the (x,y) will specify the position of the
    > bottom-right corner, borders included.
    >
    > The default win gravity is NorthWest, meaning that the upper-left corner
    > of the window -- borders included -- is placed at (x,y).
    >
    > Returning to your example, you probably have NW gravity and border_width
    > of zero, so translating (0,0) in C coordinates to R should work. You
    > then supply the mouse-move delta, and then MoveWindow.
    >
    > I suggested translating (-bw,-bw) in C to R which would be required if
    > bw were not zero. It's easy to do, and good practice. Remember that
    > the window manager is free to change bw on your top-level windows.
    >
    > The most general form would be to examine the win gravity, and pass the
    > correct C value to Translate. For example, in SE gravity you would pass
    > (width+2*bw, height+2*bw). This type of code would be required in
    > general-purpose software, such as libraries or window managers. (And
    > not all window managers get this one right; as always, check what twm
    > does before blaming your code since twm does it right.)


  6. Re: How to simulate dragging?

    Tron Thomas wrote:
    >XGetWindowAttributes always returns 0 for both the x and y fields of
    >the XWindowAttributes structure. This implies that it is not
    >returning the points relative to the frame which should, as you
    >mentioned below, something like (frame_border_width, titlebar_height).
    >
    >So, it looks like I have been doing a client to root transformation
    >when I called XTranslateCoordinates, and not a frame to root
    >transformation. It also looks like when I call XMoveWindow the window
    >gets moved to a position that is not where I told it to move to
    >because the window manager is getting in the way and adjusting the
    >window to a different location.
    >
    >I need a way to predict what the window manager is doing so I can
    >compensate and make sure the window gets places where it should be.


    One question -- what window manager? I would like to try to reproduce
    this myself.

    It comes down to your top-level window's win_gravity attribute and
    what your window manager is doing with it.

    The intended operation is explained in the ICCCM, section 4.1.2.3, but
    there is a better explanation or clarification at

    http://xserver.freedesktop.org/Standards/wm-spec

    For anything but a top-level window, the win_gravity tells the X server
    which side/corner the window is `attached to' when its parent is resized.
    For a top-level window, the parent is the root -- which cannot be
    resized -- so the field would appear to be unused. It is used, though,
    in the context of ICCCM and window manager operations.

    A FAQ in this group is that of how to get the dimensions of window manager
    decorations. For example, you want to center a dialog window over the
    main application window -- and include the wm frame in the centering
    calculation. The problem is that these dimensions are not available until
    the window is mapped, which is too late.

    This is where the seconded win_gravity field comes in. The basic idea is
    that you would specify CenterGravity on your dialog, and give the (x,y)
    coordinates of where you want the center of the dialog window -- frame
    window included -- to be positioned. The window manager is supposed to
    do the required adjustments for you.

    In your case it would seem that you want to specify StaticGravity, and
    TranslateCoordinates from (0,0). I would specify the gravity both in
    the window attributes (CreateWindow) and the WM_NORMAL_HINTS property.


  7. Re: How to simulate dragging?

    kbr@pangea.ca (Kip Rugger) wrote in message news:...
    > One question -- what window manager? I would like to try to reproduce
    > this myself.
    >
    > It comes down to your top-level window's win_gravity attribute and
    > what your window manager is doing with it.
    >
    > The intended operation is explained in the ICCCM, section 4.1.2.3, but
    > there is a better explanation or clarification at
    >
    > http://xserver.freedesktop.org/Standards/wm-spec
    >
    > For anything but a top-level window, the win_gravity tells the X server
    > which side/corner the window is `attached to' when its parent is resized.
    > For a top-level window, the parent is the root -- which cannot be
    > resized -- so the field would appear to be unused. It is used, though,
    > in the context of ICCCM and window manager operations.
    >
    > A FAQ in this group is that of how to get the dimensions of window manager
    > decorations. For example, you want to center a dialog window over the
    > main application window -- and include the wm frame in the centering
    > calculation. The problem is that these dimensions are not available until
    > the window is mapped, which is too late.
    >
    > This is where the seconded win_gravity field comes in. The basic idea is
    > that you would specify CenterGravity on your dialog, and give the (x,y)
    > coordinates of where you want the center of the dialog window -- frame
    > window included -- to be positioned. The window manager is supposed to
    > do the required adjustments for you.
    >
    > In your case it would seem that you want to specify StaticGravity, and
    > TranslateCoordinates from (0,0). I would specify the gravity both in
    > the window attributes (CreateWindow) and the WM_NORMAL_HINTS property.


    I'm not sure which window manager it is. I am running KDE
    3.1.3-0.9x.4 on RedHat Linux 9. So, whatever KDE defaults to for a
    window manager is what I'm using. I also switch to Gnome sometimes,
    and I believe I get the same results from its window manager as well.

    Anyway, I found a work around for my problem. I just enabled the
    override_redirect field of the XSetWindowAttributes and call
    XChangeWindowAttributes with the proper value mask. This elminates
    the window manager which my application doesn't need anyway as it has
    its own look and behaviour.

    So, now I am no longer having the difficulty of trying to negotiate
    the position of the window with the window manager, and window
    dragging works fine.

  8. Re: How to simulate dragging?

    Tron Thomas wrote:
    >>
    >> In your case it would seem that you want to specify StaticGravity, and
    >> TranslateCoordinates from (0,0). I would specify the gravity both in
    >> the window attributes (CreateWindow) and the WM_NORMAL_HINTS property.

    >
    >I'm not sure which window manager it is. I am running KDE
    >3.1.3-0.9x.4 on RedHat Linux 9. So, whatever KDE defaults to for a
    >window manager is what I'm using. I also switch to Gnome sometimes,
    >and I believe I get the same results from its window manager as well.
    >
    >Anyway, I found a work around for my problem. I just enabled the
    >override_redirect field of the XSetWindowAttributes and call
    >XChangeWindowAttributes with the proper value mask. This elminates
    >the window manager which my application doesn't need anyway as it has
    >its own look and behaviour.
    >
    >So, now I am no longer having the difficulty of trying to negotiate
    >the position of the window with the window manager, and window
    >dragging works fine.



    Well ignoring the problem is one way of solving it.

    I tried StaticGravity on several window managers; it worked fine for
    Gnome/Sawfish, KDE, Enlightenment, and twm. An old version of mwm
    did not handle it properly.

    Here is the test program:

    #include
    #include
    #include

    static void
    dragEH(Widget w, XtPointer clientdata, XEvent * event, Boolean * cont)
    {
    XPoint * pt = (XPoint *)clientdata;
    XMotionEvent * xm = (XMotionEvent *)event;
    XButtonEvent * xb = (XButtonEvent *)event;

    switch (event->type) {
    case MotionNotify:
    if (event->xmotion.state != Button1Mask) break;
    XtMoveWidget(w, xm->x_root - pt->x, xm->y_root - pt->y);
    XSync(XtDisplay(w), False);
    break;
    case ButtonPress:
    if (xb->button != Button1) break;
    pt->x = xb->x, pt->y = xb->y;
    break;
    }
    *cont = True;
    }

    int
    main(int argc, char * argv[])
    {
    XtAppContext ac;
    Widget shell;
    XPoint point;

    shell = XtVaOpenApplication(&ac, "Test", 0, 0, &argc, argv, 0,
    applicationShellWidgetClass,
    XtNgeometry, "100x100",
    XtNwinGravity, StaticGravity,
    XtNborderWidth, 0,
    0);

    XtAddEventHandler(shell,
    ButtonPressMask|ButtonReleaseMask|Button1MotionMas k,
    False, dragEH, (XtPointer)&point);

    XtRealizeWidget(shell);

    XtAppMainLoop(ac);
    return 0;
    }


+ Reply to Thread