Set/change mouse cursor (multi-OS)

Share your advanced PureBasic knowledge/code with the community.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Set/change mouse cursor (multi-OS)

Post by Keya »

I couldn't find a multi-OS SetCursor but the pieces of the puzzle were all there and just needed to be put together, so thankyou to everybody who contributed, especially Shardik and Oma. For OSX there's also more interesting mouse cursor options in this thread.
Currently just has CURSOR_ARROW and CURSOR_BUSY, easy to add more though - if you can find matching trios!

Example usage: SetCursor(hWnd, CURSOR_BUSY)
On OSX hWnd is ignored

SetCursor.pbi

Code: Select all

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  Macro CURSOR_ARROW : #IDC_ARROW : EndMacro
  Macro CURSOR_BUSY : #IDC_WAIT : EndMacro
  
CompilerElseIf #PB_Compiler_OS = #PB_OS_MacOS
  Macro CURSOR_ARROW : #kThemeArrowCursor : EndMacro
  Macro CURSOR_BUSY : #kThemeWatchCursor : EndMacro
  ImportC ""
    SetThemeCursor(CursorType.L)
  EndImport
  
CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
  Global *Cursor.GdkCursor
  Macro CURSOR_ARROW : #GDK_ARROW : EndMacro
  Macro CURSOR_BUSY : #GDK_WATCH : EndMacro
  ImportC ""
    gtk_widget_get_window(*widget.GtkWidget)
  EndImport
CompilerEndIf  


Procedure SetCursor(hWnd, CursorId)
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    SetClassLong_(hWnd, #GCL_HCURSOR, LoadCursor_(0, CursorId))
  CompilerElseIf #PB_Compiler_OS = #PB_OS_MacOS
    SetThemeCursor(CursorId)
  CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
  	*Cursor= gdk_cursor_new_(CursorID)
	  If *Cursor
		  gdk_window_set_cursor_(gtk_widget_get_window(WindowID(Window)), *Cursor)
	  EndIf
  CompilerEndIf
EndProcedure
SetCursorDemo.pb

Code: Select all

IncludeFile("SetCursor.pbi")

hWnd=OpenWindow(0, 0,0, 400,200, "Change Mouse Cursor", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ButtonGadget(0, 10, 10, 40, 24, "Arrow")
ButtonGadget(1, 10, 40, 40, 24, "Busy")
Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 0
          SetCursor(hWnd, CURSOR_ARROW)
        Case 1
          SetCursor(hWnd, CURSOR_BUSY)
      EndSelect
  EndSelect
ForEver

Cursor constants:

Code: Select all

;--- WINDOWS
; #IDC_ARROW=32512
; #IDC_APPSTARTING=32650
; #IDC_HAND=32649
; #IDC_HELP=32651
; #IDC_IBEAM=32513
; #IDC_CROSS=32515
; #IDC_NO=32648
; #IDC_SIZE=32640
; #IDC_SIZEALL=32646
; #IDC_SIZENESW=32643
; #IDC_SIZENS=32645
; #IDC_SIZENWSE=32642
; #IDC_SIZEWE=32644
; #IDC_UPARROW=32516
; #IDC_WAIT=32514


;--- OSX
; #kThemeArrowCursor = 0
; #kThemeCopyArrowCursor = 1
; #kThemeAliasArrowCursor = 2
; #kThemeContextualMenuArrowCursor = 3
; #kThemeIBeamCursor = 4
; #kThemeCrossCursor = 5
; #kThemePlusCursor = 6
; #kThemeWatchCursor = 7
; #kThemeClosedHandCursor = 8
; #kThemeOpenHandCursor = 9
; #kThemePointingHandCursor = 10
; #kThemeCountingUpHandCursor = 11
; #kThemeCountingDownHandCursor = 12
; #kThemeCountingUpAndDownHandCursor = 13
; #kThemeSpinningCursor = 14
; #kThemeResizeLeftCursor = 15
; #kThemeResizeRightCursor = 16
; #kThemeResizeLeftRightCursor = 17
; #kThemeNotAllowedCursor = 18


;--- LINUX:
;   GDK_X_CURSOR 		  = 0,
;   GDK_ARROW 		  = 2,
;   GDK_BASED_ARROW_DOWN    = 4,
;   GDK_BASED_ARROW_UP 	  = 6,
;   GDK_BOAT 		  = 8,
;   GDK_BOGOSITY 		  = 10,
;   GDK_BOTTOM_LEFT_CORNER  = 12,
;   GDK_BOTTOM_RIGHT_CORNER = 14,
;   GDK_BOTTOM_SIDE 	  = 16,
;   GDK_BOTTOM_TEE 	  = 18,
;   GDK_BOX_SPIRAL 	  = 20,
;   GDK_CENTER_PTR 	  = 22,
;   GDK_CIRCLE 		  = 24,
;   GDK_CLOCK	 	  = 26,
;   GDK_COFFEE_MUG 	  = 28,
;   GDK_CROSS 		  = 30,
;   GDK_CROSS_REVERSE 	  = 32,
;   GDK_CROSSHAIR 	  = 34,
;   GDK_DIAMOND_CROSS 	  = 36,
;   GDK_DOT 		  = 38,
;   GDK_DOTBOX 		  = 40,
;   GDK_DOUBLE_ARROW 	  = 42,
;   GDK_DRAFT_LARGE 	  = 44,
;   GDK_DRAFT_SMALL 	  = 46,
;   GDK_DRAPED_BOX 	  = 48,
;   GDK_EXCHANGE 		  = 50,
;   GDK_FLEUR 		  = 52,
;   GDK_GOBBLER 		  = 54,
;   GDK_GUMBY 		  = 56,
;   GDK_HAND1 		  = 58,
;   GDK_HAND2 		  = 60,
;   GDK_HEART 		  = 62,
;   GDK_ICON 		  = 64,
;   GDK_IRON_CROSS 	  = 66,
;   GDK_LEFT_PTR 		  = 68,
;   GDK_LEFT_SIDE 	  = 70,
;   GDK_LEFT_TEE 		  = 72,
;   GDK_LEFTBUTTON 	  = 74,
;   GDK_LL_ANGLE 		  = 76,
;   GDK_LR_ANGLE 	 	  = 78,
;   GDK_MAN 		  = 80,
;   GDK_MIDDLEBUTTON 	  = 82,
;   GDK_MOUSE 		  = 84,
;   GDK_PENCIL 		  = 86,
;   GDK_PIRATE 		  = 88,
;   GDK_PLUS 		  = 90,
;   GDK_QUESTION_ARROW 	  = 92,
;   GDK_RIGHT_PTR 	  = 94,
;   GDK_RIGHT_SIDE 	  = 96,
;   GDK_RIGHT_TEE 	  = 98,
;   GDK_RIGHTBUTTON 	  = 100,
;   GDK_RTL_LOGO 		  = 102,
;   GDK_SAILBOAT 		  = 104,
;   GDK_SB_DOWN_ARROW 	  = 106,
;   GDK_SB_H_DOUBLE_ARROW   = 108,
;   GDK_SB_LEFT_ARROW 	  = 110,
;   GDK_SB_RIGHT_ARROW 	  = 112,
;   GDK_SB_UP_ARROW 	  = 114,
;   GDK_SB_V_DOUBLE_ARROW   = 116,
;   GDK_SHUTTLE 		  = 118,
;   GDK_SIZING 		  = 120,
;   GDK_SPIDER		  = 122,
;   GDK_SPRAYCAN 		  = 124,
;   GDK_STAR 		  = 126,
;   GDK_TARGET 		  = 128,
;   GDK_TCROSS 		  = 130,
;   GDK_TOP_LEFT_ARROW 	  = 132,
;   GDK_TOP_LEFT_CORNER 	  = 134,
;   GDK_TOP_RIGHT_CORNER 	  = 136,
;   GDK_TOP_SIDE 		  = 138,
;   GDK_TOP_TEE 		  = 140,
;   GDK_TREK 		  = 142,
;   GDK_UL_ANGLE 		  = 144,
;   GDK_UMBRELLA 		  = 146,
;   GDK_UR_ANGLE 		  = 148,
;   GDK_WATCH 		  = 150,
;   GDK_XTERM 		  = 152
;   GDK_BLANK_CURSOR = -2
;   GDK_CURSOR_IS_PIXMAP = -1
Last edited by Keya on Thu Mar 24, 2016 4:03 pm, edited 3 times in total.
Oma
Enthusiast
Enthusiast
Posts: 312
Joined: Thu Jun 26, 2014 9:17 am
Location: Germany

Re: Set/change mouse cursor (multi-OS)

Post by Oma »

Hello Keya again!

It works fine on Linux, thank you.
But it was intended that #GDK_BLANK_CURSOR is not commented out in my list.
It is the only one that isn't predefined in PB.

Regards, Charly
PureBasic 5.4-5.7, Linux: (X/L/K)Ubuntus+Mint - Windows XP (32Bit)
PureBasic Linux-API-Library & Viewer: http://www.chabba.de
User avatar
Shardik
Addict
Addict
Posts: 1984
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Set/change mouse cursor (multi-OS)

Post by Shardik »

Keya,

thank you very much for trying to create a cross-platform routine to change the mouse cursor. Unfortunately your current solution has some problems (especially on MacOS). I know from my own hard learned experience that it's necessary to test all cross-platform code on all platforms in x86/x64/ASCII/Unicode modes to detect mistakes which I would never had expected... :wink:

Unfortunately your link to my MacOS code from June 2011 points to old code for the Carbon framework when PureBasic was solely based on the (from Apple now deprecated) Carbon framework. With PB 5.00 the Mac version of PureBasic was completely reprogrammed and based on the current Cocoa framework. For the Cocoa framework I already demonstrated this totally different example which unfortunately misses the busy cursor (you should also read the following postings).

Since your current example is based on the Carbon framework it doesn't work when compiled on MacOS X for 64 bit because the Carbon framework was never ported by Apple from 32 to 64 bit.

I also spotted a small mistake in the Linux part: you should replace

Code: Select all

        gdk_window_set_cursor_(gtk_widget_get_window(WindowID(Window)), *Cursor)
by

Code: Select all

        gdk_window_set_cursor_(gtk_widget_get_window(hWnd), *Cursor)
because your unmodified example works only by chance (if #Window = 0)...
(EnableExplicit would have been your friend to find this one... :wink:)

For the Windows part I also spotted a small mistake. You should replace SetClassLong() by SetClassLongPtr() because MSDN states:
MSDN for SetClassLong wrote:Note This function has been superseded by the SetClassLongPtr function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use SetClassLongPtr.
Some other small (although very subjective) recommendations:
- For me it's looks clearer to use CompilerSelect/CompilerCase instead of CompilerIf/CompilerElseIf
- I wouldn't use macro definitions but define custom constants using the platform specific constants

This is how your converted code would look (the problem with using the old Carbon framework code isn't solved yet, so it still doesn't work on MacOS x64). And a reader of this posting using MacOS doesn't need to lookup and insert the necessary cursor constants but can also run this code after a simple copy and paste... :wink:
Although your collection of the all the constants for the 3 platforms is very useful!

Code: Select all

EnableExplicit

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Windows
    #CURSOR_ARROW = #IDC_ARROW
    #CURSOR_BUSY = #IDC_WAIT
  CompilerCase #PB_OS_MacOS
    #CURSOR_ARROW = 0 ; #kThemeArrowCursor
    #CURSOR_BUSY = 7  ; #kThemeWatchCursor

    ImportC ""
      SetThemeCursor(CursorType.I)
    EndImport
  CompilerCase #PB_OS_Linux
    #CURSOR_ARROW = #GDK_ARROW
    #CURSOR_BUSY = #GDK_WATCH

    ImportC ""
      gtk_widget_get_window(*Widget.GtkWidget)
    EndImport
CompilerEndSelect

Procedure SetCursor(WindowID.I, CursorID.I)
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      SetClassLongPtr_(WindowID(WindowID), #GCL_HCURSOR,
        LoadCursor_(0, CursorID))
    CompilerCase #PB_OS_MacOS
      SetThemeCursor(CursorID)
    CompilerCase #PB_OS_Linux
      Protected Cursor.I

      Cursor= gdk_cursor_new_(CursorID)

      If Cursor
        gdk_window_set_cursor_(gtk_widget_get_window(WindowID(WindowID)),
          Cursor)
      EndIf
  CompilerEndSelect
EndProcedure

OpenWindow(0, 100, 100, 300, 200, "Change Mouse Cursor")
ButtonGadget(0, 10, 10, 80, 24, "Arrow")
ButtonGadget(1, 10, 40, 80, 24, "Busy")

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 0
          SetCursor(0, #CURSOR_ARROW)
        Case 1
          SetCursor(0, #CURSOR_BUSY)
      EndSelect
  EndSelect
ForEver
Another potential problem about which a user of these routines should be aware is the different behaviour after pressing the "Busy" button:
- On Linux and MacOS the cursor image is changed as soon as the "Busy" button is pressed.
- On Windows the cursor image is NOT immediately changed when the "Busy" button is pressed but when the cursor is moved away from the button. As soon as the cursor enters the "Arrow" or "Busy" button again, the arrow cursor will be restored and will only be changed to the busy cursor when not hovering over a button!
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: Set/change mouse cursor (multi-OS)

Post by Keya »

great work Shardik and thanks for the corrections! Im not able to test OSX at the moment as i have to reinstall (which takes about twenty attempts and a full weekend as OSX is soooo fussy about VMs!), but as your original OSX code was so simple i assumed it was still ok - i wasnt aware it was for old Carbon and not Cocoa though, whoops! :oops: :lol: I thought there might be more to the Windows puzzle too - all is well that ends well! lol
User avatar
Shardik
Addict
Addict
Posts: 1984
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Set/change mouse cursor (multi-OS)

Post by Shardik »

On MacOS X with the current Cocoa framework the busy or wait cursor is problematic: you won't find a busy cursor at all because a busy cursor is automatically displayed by MacOS:
Apple's [url=https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/Pointers.html][color=#0040FF]OS X Human Interface Guidelines[/color][/url] wrote:The spinning wait cursor (shown below) is also standard, but it is displayed automatically by the window server when an app can't handle all of the events it receives. In general, if an app doesn't respond for a few moments, the spinning wait cursor appears. If the app continues to be unresponsive, users often react by force-quitting it.
On Wikipedia the explanation for the missing wait cursor on MacOS X is even more detailed:
Wikipedia for [url=https://en.wikipedia.org/wiki/Spinning_pinwheel][color=#0040FF]Spinning_pinwheel[/color][/url] wrote:The display of the wait cursor is controlled by the operating system, not by the application. This works as follows:

Each application has an event queue that receives events from the operating system (For example key presses and mouse button clicks). If an application takes longer than 2 seconds[7] to process the events in its event queue (regardless of the exact cause) the operating system displays the wait cursor whenever the cursor hovers over that application's windows.

This is meant to indicate that the application is temporarily unresponsive, a state from which the application may recover, however it may also indicate that the application has entered an unrecoverable state or an infinite loop.

This prevents the user from closing, resizing, or even minimizing the windows of that application, however moving the window is still possible in OS X and previously hidden parts of the window are usually redrawn even when the application is unresponsive.

Users can choose to terminate an unresponsive application, by using "Force Quit" under the Apple menu, the keystroke command-option-escape, or the Force Quit command found by control-clicking (or right-clicking) the icon of an unresponsive application in the Dock.

While one application is unresponsive, typically other applications are usable in the meantime.
Nevertheless I have tried to find a cross-platform solution which allows you to toggle between the normal arrow cursor and a busy cursor. I have simply loaded the PNG image of a classic Windows hourglass from Wikipedia and converted it to data in a DataSection. On MacOS now this image is read in with CatchImage() to display an hourglass as wait cursor while on Linux and Windows the default cursor images are used.

So now you are able to display a wait cursor on MacOS X in 32 and 64 bit programs. But you should be warned that Apple won't like or approve this... :P :lol:

Code: Select all

EnableExplicit

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Linux
    #CURSOR_ARROW = #GDK_ARROW
    #CURSOR_BUSY = #GDK_WATCH

    ImportC ""
      gtk_widget_get_window(*Widget.GtkWidget)
    EndImport
  CompilerCase #PB_OS_MacOS
    UsePNGImageDecoder()
    Define Hotspot.NSPoint
    CatchImage(0, ?HourglassImagePNG)
  CompilerCase #PB_OS_Windows
    #CURSOR_ARROW = #IDC_ARROW
    #CURSOR_BUSY = #IDC_WAIT
CompilerEndSelect

Define BusyCursorActive.I
Define NewCursor.I

OpenWindow(0, 270, 100, 200, 90, "Toggle busy cursor")
ButtonGadget(0, 20, 30, 160, 25, "Enable busy cursor")
  
Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventGadget() = 0 And EventType() = #PB_EventType_LeftClick
        BusyCursorActive ! 1

        CompilerSelect #PB_Compiler_OS
          CompilerCase #PB_OS_Linux ; -----------------------------------------
            If BusyCursorActive
              NewCursor = gdk_cursor_new_(#CURSOR_BUSY)
            Else
              NewCursor = gdk_cursor_new_(#CURSOR_ARROW)
            EndIf

            If NewCursor
              gdk_window_set_cursor_(gtk_widget_get_window(WindowID(0)), NewCursor)
            EndIf
          CompilerCase #PB_OS_MacOS ; -----------------------------------------
            If BusyCursorActive
              Hotspot\x = 0
              Hotspot\y = 0
              NewCursor = CocoaMessage(0, 0, "NSCursor alloc")
              CocoaMessage(0, NewCursor, "initWithImage:", ImageID(0),
                "hotSpot:@", @Hotspot)
            Else
              NewCursor = CocoaMessage(0, 0, "NSCursor arrowCursor")
            EndIf

             If NewCursor            
               CocoaMessage(0, NewCursor, "set")
             EndIf
          CompilerCase #PB_OS_Windows ; ---------------------------------------
            If BusyCursorActive
              NewCursor = LoadCursor_(0, #CURSOR_BUSY)
            Else
              NewCursor = LoadCursor_(0, #CURSOR_ARROW)
            EndIf

            If NewCursor
              SetClassLongPtr_(WindowID(0), #GCL_HCURSOR, NewCursor)
            EndIf
        CompilerEndSelect ; ---------------------------------------------------
        
        If BusyCursorActive
          SetGadgetText(0, "Disable busy cursor")
        Else
          SetGadgetText(0, "Enable busy cursor")
        EndIf
      EndIf
  EndSelect
ForEver

End

DataSection
  HourglassImagePNG:
  Data.Q $0A1A0A0D474E5089,$524448490D000000,$180000000F000000,$A705250000000608,$4343692404000069
  Data.Q $6F72502043434950,$11380000656C6966,$3E1454DB6FDF5585,$58203F16A4526F89,$5B5355AFC58A8747
  Data.Q $934906C6AD1A1BB9,$2AD8E9A5164AEDA5,$071BA989373AE424,$37817B4FAAB6E9DB,$480F03D94001FC06
  Data.Q $F6D97B62060D213C,$49AA2A875349B4C0,$ED26210FC4E87B48,$53276276BBE15505,$39DF39CBFAF55CC4
  Data.Q $5F3D44DB5EE73BE7,$AB968855199AB569,$16A7939524CF9DAE,$4BD4B3D28A4D9E94,$CB912D4EE9ABD403
  Data.Q $EBCEF715C12E12CD,$EE91EDCB088A1DE1,$DC8B7ABF23F64EFE,$1D156EC04F2289D1,$6BD4CC510CF8197D
  Data.Q $69F8FB07BF144BB6,$36D3F01CF3DC06B7,$129C7D9702AB0204,$3F214E3D27C7D178,$08AD15EB2AD08E09
  Data.Q $B72F66D8BC3C06BC,$55E193C818035F61,$ADB3916622BA1B6E,$718F2FE1CF726192,$F4759B2E0FFF31B7
  Data.Q $B9A59CFB58835DEC,$54F62BDE890FB863,$F01CF4B5D73F89BE,$7F6149B9AF5FB04B,$78027CD2FA8FF805
  Data.Q $DE747D92A9F4511F,$06DE14AD5F5F47E7,$4D0BA770D17B05DE,$B1181C7B31756AFB,$3823C898EB25F5D1
  Data.Q $8017A4B3126775D7,$91E863D8B4F856EF,$C1E061E06953C8A8,$56719A5F99AA7D4A,$CC995AB3CBDD84E6
  Data.Q $E4CDA7BF648F2302,$A8BCADB43F800780,$16A233373AD2CD05,$41A9AE6E6B574BF2,$32FD0B51599AAEDA
  Data.Q $BB6D29C88F5E3B99,$76BA0ECB1F94C295,$E9D1C64A16CB19A1,$DA11677A66AD7F26,$9F90DA179EBB05D8
  Data.Q $D879180674ECD2DD,$DB1C06E62F3ABC3F,$47C0A3B66252D45D,$622D164E234444E8,$329E50A43B4AA9D5
  Data.Q $32253C3646BDC494,$223161CBC38584C8,$8F3979DD6C12D24F,$2DBD17B28C84C7E3,$BF70AFC73BA551A2
  Data.Q $FC01D9533F58B1C9,$13671B30ECB3621F,$4B08EC9BD837B0A4,$6D720A050EC13AC1,$F3F5287DD32A153A
  Data.Q $504E3A1654754FBC,$CE5188287D40FCF2,$86FE8FCDFA1AEFD9,$D04F23F42B4BB09A,$13072013ED5B3431
  Data.Q $6B6BA73ED2377568,$F8BB06CA3C0EEC3F,$71FC746B6BBEEDCE,$B036C76D8BDD8DF3,$B1AFD8F8C2B6C66E
  Data.Q $7A603B02526DFC2D,$09C276F0CAA1968A,$25D3015C3D1D2378,$52DAD059E06FEB0D,$D081F9EAA5A3B1DA
  Data.Q $D2CC3EC83FC12623,$A9FEA2FEAAD0E1B9,$3BDFD417A85FA86E,$2A7D24A63B19846A,$2F7D277D28FD2B7D
  Data.Q $FD22BA59748A4CFD,$39052E91BE955D24,$AF7BD8259FBDEFBB,$D5685D8ADD13615F,$7BCA4E5327359F6B
  Data.Q $B2FCBCFC94E517E4,$931E5FB914B01B3C,$CDF609ECF0BDE4A7,$B30A7418BD1DFD0C,$1804F12D7BB54D6A
  Data.Q $C6D7069D06250F34,$DB591674AB84C554,$D8C6C83652A6939F,$96E59C5C76A723F4,$8948278A9E3A788A
  Data.Q $0233E2C7C44F8BEF,$B026F85EF8BC9EB7,$3A301393FEF503A6,$33C8254C2C6DE03A,$0AF44556713A9DE2
  Data.Q $DF067E5DCDD09B74,$CA36D676AB49A20E,$0929AFAAAFD95715,$8747D55D32B8CA7C,$3628E5CF1534CD15
  Data.Q $BE24A38BC2BDB877,$BEFBDE41E88E2283,$445BEE6D0CDD7645,$5B6856EB3BC2FF87,$1ABFF510ED7D13A8
  Data.Q $A233E67C4EF086DA,$A3E7E2BDBAF5078B,$DFB03A539106E448,$B774DDE24BE91DFB,$4427CF55F78078D1
  Data.Q $1A37AEFF1A371F3B,$F365D116FF225F3B,$2B1B6C787100593F,$5948700900000013,$0B0000130B000073
  Data.Q $0000189C9A000113,$113854414449D400,$040C30830ECB94AD,$4B85B9FFFFA4D569,$91F4A18DAD1354B5
  Data.Q $D31231AF1DE24E03,$3E3DAD6D88BAC3F4,$3CD0E6FDCB2CB1AD,$1F71F7BDF72C7ECF,$5B6D66C1C50C4F60
  Data.Q $B98B2D59D67EB579,$31CFDA612AEDF1EA,$8331E817DAB0949B,$F3D02A252718073A,$06AEF7C0E9818CD2
  Data.Q $1024CE0FA4B0C807,$5D7E7056A05E48E6,$3383694190280EC5,$CF7BDEAE71B8D9F0,$A79201881067383D
  Data.Q $148F0221024CE1B3,$205311067383FDE3,$B9C4A051488C03E7,$1B02A3C1D84A6B80,$586303AC0AF702B8
  Data.Q $24FE1686BDBBD2DE,$3D80419F01552079,$2224027BBD7F8151,$0000253E8FBDD066,$42AE444E45490000
  Data.Q $0000000000008260
EndDataSection
mestnyi
Addict
Addict
Posts: 995
Joined: Mon Nov 25, 2013 6:41 am

Re: Set/change mouse cursor (multi-OS)

Post by mestnyi »

If you do like this will be visually identical in linux or windows. :)

Code: Select all

EnableExplicit

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Linux
    #CURSOR_ARROW = #GDK_ARROW
    #CURSOR_BUSY = #GDK_WATCH

    ImportC ""
      gtk_widget_get_window(*Widget.GtkWidget)
    EndImport
  CompilerCase #PB_OS_Windows
    #CURSOR_ARROW = #IDC_ARROW
    #CURSOR_BUSY = #IDC_WAIT
CompilerEndSelect

Define BusyCursorActive.I
Define NewCursor.I

OpenWindow(0, 270, 100, 200, 90, "Toggle busy cursor")
ButtonGadget(0, 20, 30, 160, 25, "Enable busy cursor")
  
Repeat
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Linux ; -----------------------------------------
      Define *Widget.GtkWidget = gdk_window_at_pointer_(0,0)
      If *Widget And PeekS(gtk_widget_get_name_(*Widget\name), -1, #PB_UTF8) = "GtkFixed"
        If NewCursor
          gdk_window_set_cursor_(gtk_widget_get_window(WindowID(0)), NewCursor)
        EndIf
      Else
        gdk_window_set_cursor_(gtk_widget_get_window(WindowID(0)), gdk_cursor_new_(#CURSOR_ARROW))
      EndIf
    CompilerEndSelect
    
    Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventGadget() = 0 And EventType() = #PB_EventType_LeftClick
        BusyCursorActive ! 1

        CompilerSelect #PB_Compiler_OS
          CompilerCase #PB_OS_Linux ; -----------------------------------------
            If BusyCursorActive
              NewCursor = gdk_cursor_new_(#CURSOR_BUSY)
            Else
              NewCursor = gdk_cursor_new_(#CURSOR_ARROW)
            EndIf

            If NewCursor
              gdk_window_set_cursor_(gtk_widget_get_window(WindowID(0)), NewCursor)
            EndIf
         
          CompilerCase #PB_OS_Windows ; ---------------------------------------
            If BusyCursorActive
              NewCursor = LoadCursor_(0, #CURSOR_BUSY)
            Else
              NewCursor = LoadCursor_(0, #CURSOR_ARROW)
            EndIf

            If NewCursor
              SetClassLongPtr_(WindowID(0), #GCL_HCURSOR, NewCursor)
            EndIf
        CompilerEndSelect ; ---------------------------------------------------
        
        If BusyCursorActive
          SetGadgetText(0, "Disable busy cursor")
        Else
          SetGadgetText(0, "Enable busy cursor")
        EndIf
      EndIf
  EndSelect
ForEver

End

SeregaZ
Enthusiast
Enthusiast
Posts: 617
Joined: Fri Feb 20, 2009 9:24 am
Location: Almaty (Kazakhstan. not Borat, but Triple G)
Contact:

Re: Set/change mouse cursor (multi-OS)

Post by SeregaZ »

maybe some one know how to load cursor from datasection? (windows case) some examples use LoadImage_ - but it use library, not memory.

and no any way to use more smart cursor changing exept SetClassLong_ or SetClassLongPtr_? i want to change cursor only for click-time, but he change forever, if i dont move mouse. half idea is make move mouse per 1px by code. user almost didnt see it, but in theory it will change cursor same time.

for first task probably i get command :) CreateIconFromResource - will study it.
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Re: Set/change mouse cursor (multi-OS)

Post by pdwyer »

Playing with this, it works quite well but if a window has gadgets covering most of it then the mouse is mostly not looking like it's busy.
The canvas gadget seems to have it's own capability for setting the cursor busy but other gadgets don't.

I would just like to set the mouse cursor to be busy for the app while it loads and processes data that may take 20secs. cross platform (linux) would be nice too but windows for now

There isn't some global way to control the cursor for an app?
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
BarryG
Addict
Addict
Posts: 3268
Joined: Thu Apr 18, 2019 8:17 am

Re: Set/change mouse cursor (multi-OS)

Post by BarryG »

pdwyer wrote: Sun Sep 05, 2021 4:34 amThere isn't some global way to control the cursor for an app?
For Windows, the only way I found to do it (didn't test much) was like this:

Code: Select all

OpenWindow(0, 200, 200, 400, 200, "Change Mouse Cursor", #PB_Window_SystemMenu)
ButtonGadget(0, 10, 10, 40, 24, "Arrow")
ButtonGadget(1, 10, 40, 40, 24, "Busy")

Repeat
  Select WindowEvent()
    Case 0
      Sleep_(1)
      If wait
        SetCursor_(wait)
      EndIf
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 0
          wait=0
          SetCursor_(0)
        Case 1
          wait=LoadCursor_(0, #IDC_WAIT)
      EndSelect
  EndSelect
ForEver
Last edited by BarryG on Sun Sep 05, 2021 7:10 am, edited 1 time in total.
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Re: Set/change mouse cursor (multi-OS)

Post by pdwyer »

Thanks, I think I can work with this.

:)
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
BarryG
Addict
Addict
Posts: 3268
Joined: Thu Apr 18, 2019 8:17 am

Re: Set/change mouse cursor (multi-OS)

Post by BarryG »

@pdwyer: Edited my post, to reduce CPU use during the main event when "wait=0".
mestnyi
Addict
Addict
Posts: 995
Joined: Mon Nov 25, 2013 6:41 am

Re: Set/change mouse cursor (multi-OS)

Post by mestnyi »

In macOS, when I click on the button, the cursor changes and if I leave it, the cursor does not change. If I click on the canvas, the cursor also changes, but when I leave it, it resets.
I would like the cursor to remain unchanged on the canvas, someone can help me with this?

Code: Select all

EnableExplicit

CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Windows
    ;--- WINDOWS
    ; #IDC_ARROW=32512
    ; #IDC_HAND=32649
   
    #CURSOR_ARROW = #IDC_CROSS
    #CURSOR_HAND = #IDC_HAND
    
  CompilerCase #PB_OS_MacOS
    #kThemeArrowCursor = 0
    #kThemePointingHandCursor = 10
    
    #CURSOR_ARROW = #kThemeArrowCursor
    #CURSOR_HAND = #kThemePointingHandCursor
    
    ImportC ""
      SetThemeCursor(CursorType.i)
    EndImport
    
  CompilerCase #PB_OS_Linux
    ;--- LINUX:
;   GDK_ARROW 		  = 2,
;   GDK_HAND1 		  = 58,
;   GDK_HAND2 		  = 60,
    
    #CURSOR_ARROW = #GDK_ARROW
    #CURSOR_HAND = #GDK_HAND1
    
    ImportC ""
      gtk_widget_get_window(*Widget.GtkWidget)
    EndImport
CompilerEndSelect

Procedure SetCursor(WindowID.I, CursorID.I)
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      SetClassLongPtr_(WindowID(WindowID), #GCL_HCURSOR, LoadCursor_(0, CursorID))
    CompilerCase #PB_OS_MacOS
      SetThemeCursor(CursorID)
      SetGadgetAttribute( WindowID.I, #PB_Canvas_Cursor, #PB_Cursor_Hand )
      
      ;CursorID = CocoaMessage(0, 0, "NSCursor arrowCursor")
      ;CursorID = CocoaMessage(0, 0, "NSCursor pointingHandCursor")
      ;CocoaMessage(0, CursorID, "set")
    CompilerCase #PB_OS_Linux
      Protected Cursor.I = gdk_cursor_new_(CursorID)
      
      If Cursor
        gdk_window_set_cursor_(gtk_widget_get_window(WindowID(WindowID)), Cursor)
      EndIf
  CompilerEndSelect
EndProcedure

OpenWindow(0, 100, 100, 300, 200, "Change Mouse Cursor")
ButtonGadget(0, 10, 10, 80, 24, "Hand")
ButtonGadget(1, 10, 40, 80, 24, "Arrow")
CanvasGadget(2, 100, 10, 80, 24)

#Button1  = 10
#Button2  = 11
#Splitter = 12

CanvasGadget(#Button1, 0, 0, 0, 0)     ; as they will be sized automatically
StringGadget(#Button2, 0, 0, 0, 0, "") ; No need to specify size or coordinates
SplitterGadget(#Splitter, 105, 70, 120, 120, #Button1, #Button2, #PB_Splitter_Separator)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 11,2
          Select EventType()
            Case #PB_EventType_LeftButtonDown 
              SetCursor(EventGadget(), #CURSOR_HAND)
;             Case #PB_EventType_MouseEnter 
;               SetCursor(EventGadget(), #CURSOR_HAND)
;             Case #PB_EventType_MouseLeave 
;               SetCursor(EventGadget(), #CURSOR_HAND)
              
          EndSelect
        Case 0
          SetCursor(EventGadget(), #CURSOR_HAND)
        Case 1
          SetCursor(EventGadget(), #CURSOR_ARROW)
      EndSelect
  EndSelect
ForEver
mestnyi
Addict
Addict
Posts: 995
Joined: Mon Nov 25, 2013 6:41 am

Re: Set/change mouse cursor (multi-OS)

Post by mestnyi »

I found a solution. all turned out to be wrong canvas event handling.
The internal mouse event occurs after the external mouse event. thus, an externally set cursor is replaced by an internally set cursor. We'll have to wait until Fred fixes it.
Post Reply