hidden cursor

Sometimes you need to hide the cursor. E.g., a presentation application in which the cursor only shows up when you move it to highlight something, then disappears. From reading the discussion on c.l.t, the following should work in Linux:

 proc show_cursor {} { .c conf -cursor "" }
 proc hide_cursor {} { .c conf -cursor { gumby blue red } }

On my Debian Box, this shows Gumby, so not a success. It is even less successful under Windows:

 Error: bad cursor spec " gumby blue red "

The documented "no" cursor doesn't work on Linux or Windows either.

AMG: I believe the "no" cursor is supposed to be a circle with a diagonal slash through it.


You can instead use a custom cursor which is invisible.

In Unix, save the following as none.cur:

 #define none_width 1
 #define none_height 1
 #define none_x_hot 0
 #define none_y_hot 0
 static unsigned char none_bits[] = {
    0x00};

Then use:

 proc show_cursor {} { .c conf -cursor "" }
 proc hide_cursor {} { .c conf -cursor "@path_to_cursor/none.cur black" }

For windows, you will have to find one for yourself on the net (e.g., a google search for invisible.cur), or use the tools suggested in custom cursors to build one. The cursor file must be on a system path, not in a VFS path.

 proc show_cursor {} { .c conf -cursor "" }
 proc hide_cursor {} { .c conf -cursor "@path_to_cursor/invisible.cur" }

AMG: Since this page is Google's #1 hit for "invisible.cur", and a real invisible cursor is hard to locate ('cuz you can't see it?), I have placed one on my Web site: http://andy.junkdrome.org/devel/tcl/invisible.cur . (This is a hopefully permanent mirror of the invisible cursor I found at http://www.scs.ryerson.ca/~cps613/CPS613/W05/Lab3/ .)

peterc 2007-03-06: The file at ioioio.net seems missing. I've put up a 32x32 blank.cur (along with similar for XBM, GIF and PNG) here: [L1 ] (link updated on 2008-01-26).

AMG: Okay, I fixed the link given above. I can't host it under the name ioioio.net anymore, since it was stolen from me. :^( Hopefully I'll have better luck holdong on to andy.junkdrome.org . ;^)

EG 2008-03-26 Note that 8.5 has a documented "none" cursor, so this trickery is not needed anymore.

AMG: Wonderful news! Finally we have a portable way of hiding the cursor.


On windows, user32.dll has the function ShowCursor(bool) which allows you to hide/show the cursor for all windows, but there is no easy way to call it unless you are already building a compiled application.


You can also simulate a hidden cursor with pointer warping, moving the cursor to the corner of the screen on hide, and restoring its position on show. This works reasonably well if the canvas covers the entire screen. If not, you can use a global grab so that the next motion event will still arrive in your window. Uncomment the grab set/release commands to activate this.

 set hidden {} ;# last cursor coordinates
 set hiding 0 ;# hiding, so don't try to unhide
 proc show_cursor {} {
     # suppress the motion event that occurs when warping
     if $::hiding return
     if [llength $::hidden] {
         foreach {x y} $::hidden break
         set ::hidden {}
         event generate {} <Motion> -warp 1 -x $x -y $y
         # grab release .c
     }
 }
 proc hide_cursor {} {
     if [llength $::hidden] return
     set ::hidden [winfo pointerxy .c]
     set ::hiding 1
     # almost to the corner --- if you go to the corner, 
     # you will miss half the motion events
     event generate {} <Motion> -warp 1 -x [expr {[winfo screenwidth .]-1}] -y 1
     update
     set ::hiding 0
     # grab set -global .c
     ## Global grab safety valve
     ## after cancel { grab release .c }
     ## after 5000 { grab release .c }
 }

Here is a small program to test whichever hide/show cursor you use:

 if 0 {
   # full screen canvas
   pack [canvas .c -width [winfo screenwidth .] -height [winfo screenheight .]]
   wm overrideredirect . 1
 } else {
   # windowed canvas
   pack [canvas .c]
 }

 # escape by clicking mouse
 bind all <1> exit

 # make the cursor appear when the mouse moves
 bind .c <Motion> {
     show_cursor
     # reschedule hide
     after cancel hide_cursor
     after 1000 hide_cursor
 }

 # Don't hide the cursor when not in the window. I think we need
 # two after cancel's because Enter and Motion both generate an after.
 bind .c <Enter> { after 1000 hide_cursor }
 bind .c <Leave> { after cancel hide_cursor; after cancel hide_cursor }

The Enter/Leave bindings are only needed for the pointer warping solution. With the invisible cursor solution, it doesn't matter which cursor you are using when you are outside the window.