Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to specify custom cursor images? #39

Open
flopp opened this issue Jan 13, 2020 · 17 comments
Open

How to specify custom cursor images? #39

flopp opened this issue Jan 13, 2020 · 17 comments

Comments

@flopp
Copy link

flopp commented Jan 13, 2020

Is there any way for the drag source to specify a custom cursor image when starting a drag operation
(e.g. a dynamically created thumbnail representation of the dragged object/data)?

@petasis
Copy link
Owner

petasis commented Jan 13, 2020

Under which platform?

@flopp
Copy link
Author

flopp commented Jan 14, 2020

Under which platform?

Both Linux and Windows ;)

If this is not possible, is there a way to statically replace the used cursor images during compile time (again both for Linux and Windows)? For Unix, I guess editing Cursors.c would work...

@flopp
Copy link
Author

flopp commented Jan 21, 2020

Do you have a solution for either Linux or Windows?

@petasis
Copy link
Owner

petasis commented Jan 28, 2020

TkDND version 1.x used to support custom icons. In the 2.x series it is not supported at all. The problem is that a cross-platform API is the main focus. So, I need to design such a cross platform API.

If you could propose an API, what would it be?

If I remember correctly:

  1. I think mac OS does this automatically (does it?)
  2. Windows has a helper functionality, that takes a "screenshot" and drags it around.
  3. Linux has nothing, it has to be implemented from scratch.

What do you want your application to do? Starting a drag begins always on a Tk window. How do you imagine this image to be created?

@petasis
Copy link
Owner

petasis commented Jan 28, 2020

You also mentioned cursors. This is an entirely different issue: drags can have both a cursor and an image.

@flopp
Copy link
Author

flopp commented Jan 30, 2020

In our application (http://www.concept.de/StarVision.html), DnD is one of the main ways for the user to interact with the GUI, e.g. you can select an drag circuit elements from one view and drop the into another view.
In the current version we're using an ancient modified copy of TkDND (probably from the old 1.x days). We're changing the cursor depending on the number and type of dragged objects - e.g. if your dragging a single AND-gate, the cursor changes to a (static, precomputed) cursor image depicting an single AND-gate, if your dragging multiple gates the cursor changes to a (static, precomputed) cursor image depicting several gates - just to give the user feedback on what's going on.
Note that we're modifying the actual cursor shape and are not dragging an image around.
So, in our application, the cursor does not really depend on the "action" (copy, move, ...), but rather on the type of objects(s) dragged.

We are in the process of updating or software to use the latest upstream TkDND version - everything's working fine (and the needed glue code even got a lot simpler), except the "change the cursor" functionality.

@petasis
Copy link
Owner

petasis commented Feb 3, 2020

Thank you very much, now I understand better! Just a question: how do you load these cursors in the application under the operating systems you need to support?
Are you using the @SourceName under windows, and the equivalent under unix?

@flopp
Copy link
Author

flopp commented Feb 4, 2020

We're doing low-level stuff to load/set cursors:

On X11/Linux, cursors are loaded via (Cursor)(Tk_GetCursorFromData(...) with static bitmap data from a bunch of XBM files. They are selected/set with XChangeActivePointerGrab(...) in the XdndStatus handler.

On Windows, we transform the same static XBM images to Windows compatible bit-patterns, which we then feed to the Windows API's CreateCursor function. The cursor is set by calling the Windows API's SetCursor function in IDropSource::GiveFeedback.

@petasis
Copy link
Owner

petasis commented Feb 4, 2020

Are these cursors available as a cursor Tk can use? I am trying to think of a way to give these cursors to tkdnd. One way is to create these cursors inside tkdnd, so tkdnd can assign names (and use them).

@petasis
Copy link
Owner

petasis commented Feb 4, 2020

Can you share the parts of the code you create the cursors under windows & unix, to put them in tkdnd?

@flopp
Copy link
Author

flopp commented Feb 14, 2020

Our Unix code is basically what's already in unix/Cursors.c, but with custom, static cursor XBMs.

On Windows, we're transforming these static XBMs into Windows compatible AND/XOR-planes and then create a HCURSOR from that:

static void xbm_to_win(
    unsigned char* sSrc, unsigned char* sMask,
    int sCols, int sRows,
    unsigned char* andCurs, unsigned char* xorCurs,
    int dCols, int dRows)
{
    for (int row = 0; row < dRows; row++) {
        for (int col = 0; col < dCols; col++) {
            char f, b;
            if ((row < sRows) && (col < sCols)) {
                f = sSrc[(row * sCols) + col];
                f = ((f & 0x01) << 7) | ((f & 0x80) >> 7) |
                    ((f & 0x02) << 5) | ((f & 0x40) >> 5) |
                    ((f & 0x04) << 3) | ((f & 0x20) >> 3) |
                    ((f & 0x08) << 1) | ((f & 0x10) >> 1);
                b = sMask[(row * sCols) + col];
                b = ((b & 0x01) << 7) | ((b & 0x80) >> 7) |
                    ((b & 0x02) << 5) | ((b & 0x40) >> 5) |
                    ((b & 0x04) << 3) | ((b & 0x20) >> 3) |
                    ((b & 0x08) << 1) | ((b & 0x10) >> 1);
            } else {
                f = 0;
                b = 0;
            }
            andCurs[(row * dCols) + col] = ~b;
            xorCurs[(row * dCols) + col] = ~f & b;
        }
    }
}

/*----------------------------------------------------------------------
 * create_cursor - create a windows cursor from xbm data.
 *----------------------------------------------------------------------
 */
static HCURSOR create_cursor(
    unsigned char* source_bits, unsigned char* mask_bits,
    int width, int height,
    int x_hot, int y_hot)
{
    HCURSOR        hCursor = NULL;
    int            x_dim   = GetSystemMetrics(SM_CXCURSOR);
    int            y_dim   = GetSystemMetrics(SM_CYCURSOR);
    int            xc      = x_dim / (sizeof(char) * CHAR_BIT);
    int            yc      = y_dim;
    unsigned char* andCurs = (unsigned char*)malloc(xc * yc);
    unsigned char* xorCurs = (unsigned char*)malloc(xc * yc);

    if (andCurs && xorCurs) {
        xbm_to_win(
            source_bits, mask_bits,
            width / (sizeof(char) * CHAR_BIT), height,
            andCurs, xorCurs,
            xc, yc);
        hCursor = CreateCursor(
            Tk_GetHINSTANCE(),
            x_hot, y_hot,
            x_dim, y_dim,
            (void*)andCurs, (void*)xorCurs);
    }

    free(andCurs);
    free(xorCurs);

    return hCursor;
}

On Windows, the cursor current cursor is controlled by the drag source, via SetCursor in IDragSource::GiveFeedback.

@petasis
Copy link
Owner

petasis commented Feb 23, 2020

I have committed some changes that add support for custom images under linux (build it with cmake).
I have created a sample script (in demos/custom_cursors.tcl) that shows two possible methods for using a custom cursor:

  • specify a map, i.e. from copy -> "to your Tk cursor"
  • provide a callback, that will return a Tk cursor.

The example uses both, the callback is called after the map.

Do you think that such an interface will work in your case? I mean having the files loaded from a folder, as Tk wants?

@petasis
Copy link
Owner

petasis commented Feb 23, 2020

Make sure you have Xcursor lib & dev files installed, as I have written some code that uses also this (but needs work as I have no idea if the cursors are cached...)

@flopp
Copy link
Author

flopp commented Feb 25, 2020

Thanks, I'll look into it.

@flopp
Copy link
Author

flopp commented Jun 18, 2020

I finally managed to integrate the custom cursor code into our application. Works great - even with loading cursor images from a virtual file-system (a zip-based file-system wrapped into the application binary).

Here's a quick screencast of the custom cursors in action:
https://youtu.be/eJ0GUjk-Fdg

@petasis
Copy link
Owner

petasis commented Aug 18, 2020

Nice. Is there anything that cannot be done with the current code?

@flopp
Copy link
Author

flopp commented Sep 2, 2020

No, we're quite happy with the current implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants