Page 1 of 1

touch tablet

Posted: Wed Nov 12, 2003 12:39 am
by blueznl

Code: Select all

; purebasic survival guide
; touch_tablet - 11.11.2003 ejn (blueznl)
; http://www.xs4all.nl/~bluez/datatalk/pure1.htm
;
; - using a touch tablet in purebasic
; - references to pages / tables refer to a word document by lcs/telegraphics:
;
;     wintab interface specification 1.0
;     16- and 32-bit api reference by Rick Poyner
;     revised december 13, 1994
;
; - use google, the doc above is NOT part of the wintab sdk (duh!) but is on their site
; - to do: disable tablet context when window is not in focus
; - to do: handle pen vs. lmb issues (pen can generate lmb message at pressure 0, timing is all...)
; - to do: tilt detection (wacom) etc.
;
;
; 
IncludeFile("x_lib.pb")
;
x_init()
;
; wanna' have a message to notify the mouse leaving the window? use the trackmouseevent_() function
; here's the structure and the constants we might need later (uncomment when needed)
;
;   Structure tagTRACKMOUSEEVENT
;     cbSize.l
;     dwFlags.l
;     hwndTrack.l
;     dwHoverTime.l
;   EndStructure
;   #TME_LEAVE = 2
;
#WM_MOUSELEAVE = $2A3
;
;
; *** wintab constants and structures
;
; device configuration constants (ch5.7.1 p21)
;
#WTDC_NONE = 0
#WTDC_CANCEL = 1
#WTDC_OK = 2
#WTDC_RESTART = 3
;     
; message constants (ch6 p34)
;
#WT_DEFBASE = $7FF0
#WT_MAXOFFSET = $F
#WT_MAX = $7FFF                              ; aka wt_defbase + wt_maxoffset
Enumeration #WT_DEFBASE
  #WT_PACKET
  #WT_CTXOPEN
  #WT_CTXCLOSE
  #WT_CTXUPDATE
  #WT_CTXOVERLAP
  #WT_PROXIMITY
  #WT_INFOCHANGE
  #WT_CSRCHANGE
EndEnumeration
;  
; packet constants (t7.1 p38)
;
#PK_CONTEXT = $1                            ; context
#PK_STATUS = $2                             ; status bits
#PK_TIME = $4                               ; time stamp
#PK_CHANGED = $8                            ; change bit vector
#PK_SERIAL_NUMBER = $10                     ; packet serial number
#PK_CURSOR = $20                            ; reporting cursor
#PK_BUTTONS = $40                           ; button information
#PK_X = $80                                 ; x-axis
#PK_Y = $100                                ; y-axis
#PK_Z = $200                                ; z-axis
#PK_NORMAL_PRESSURE = $400                  ; normal or tip pressure
#PK_TANGENT_PRESSURE = $800                 ; tangential or barrel pressure
#PK_ORIENTATION = $1000                     ; orientation info / tilts
#PK_ROTATION = $2000        
;
; unit specifiers (t7.2)
;
#TU_NONE = 0
#TU_INCHES = 1
#TU_CENTIMETERS = 2
#TU_CIRCLE = 3
;
; wti category definitions (t7.3)
;
#WTI_INTERFACE = 1
#WTI_STATUS = 2
#WTI_DEFCONTEXT = 3
#WTI_DEFSYSCTX = 4
#WTI_DEVICES = 100
#WTI_CURSORS = 200
#WTI_EXTENSIONS = 300
#WTI_DDCTXS = 400
#WTI_DSCTXS = 500
;
; wti_interface index definitions (t7.4)
;
#IFC_WINTABID = 1
#IFC_SPECVERSION = 2
#IFC_IMPLVERSION = 3
#IFC_NDEVICES = 4
#IFC_NCURSORS = 5
#IFC_NCONTEXTS = 6
#IFC_CTXOPTIONS = 7
#IFC_CTXSAVESIZE = 8
#IFC_NEXTENSIONS = 9
#IFC_NMANAGERS = 10
#IFC_MAX = 10
;
; wti_status index definitions (t7.5)
;
#STA_CONTEXTS = 1
#STA_SYSCTXS = 2
#STA_PKTRATE = 3
#STA_PKTDATA = 4
#STA_MANAGERS = 5
#STA_SYSTEM = 6
#STA_BUTTONUSE = 7
#STA_SYSBTNUSE = 8
#STA_MAX = 8
;
; wti_defcontext / wti_defsysctx index definitions (t7.6)
;
#CTX_NAME = 1
#CTX_OPTIONS = 2
#CTX_STATUS = 3
#CTX_LOCKS = 4
#CTX_MSGBASE = 5
#CTX_DEVICE = 6
#CTX_PKTRATE = 7
#CTX_PKTDATA = 8
#CTX_PKTMODE = 9
#CTX_MOVEMASK = 10
#CTX_BTNDNMASK = 11
#CTX_BTNUPMASK = 12
#CTX_INORGX = 13
#CTX_INORGY = 14
#CTX_INORGZ = 15
#CTX_INEXTX = 16
#CTX_INEXTY = 17
#CTX_INEXTZ = 18
#CTX_OUTORGX = 19
#CTX_OUTORGY = 20
#CTX_OUTORGZ = 21
#CTX_OUTEXTX = 22
#CTX_OUTEXTY = 23
#CTX_OUTEXTZ = 24
#CTX_SENSX = 25
#CTX_SENSY = 26
#CTX_SENSZ = 27
#CTX_SYSMODE = 28
#CTX_SYSORGX = 29
#CTX_SYSORGY = 30
#CTX_SYSEXTX = 31
#CTX_SYSEXTY = 32
#CTX_SYSSENSX = 33
#CTX_SYSSENSY = 34
#CTX_MAX = 34
;     
; wti_devices index definitions (t7.7)
;
#DVC_NAME = 1
#DVC_HARDWARE = 2
#DVC_NCSRTYPES = 3
#DVC_FIRSTCSR = 4
#DVC_PKTRATE = 5
#DVC_PKTDATA = 6
#DVC_PKTMODE = 7
#DVC_CSRDATA = 8
#DVC_XMARGIN = 9
#DVC_YMARGIN = 10
#DVC_ZMARGIN = 11
#DVC_X = 12
#DVC_Y = 13
#DVC_Z = 14
#DVC_NPRESSURE = 15
#DVC_TPRESSURE = 16
#DVC_ORIENTATION = 17
#DVC_ROTATION = 18
#DVC_PNPID = 19
#DVC_MAX = 19
;     
; dvc_hardware hardware capabilities (t7.7)
;
#HWC_INTEGRATED = $1
#HWC_TOUCH = $2
#HWC_HARDPROX = $4
#HWC_PHYSID_CURSORS = $8
;       
; wti_cursors (t7.8)
;
#CSR_NAME = 1
#CSR_ACTIVE = 2
#CSR_PKTDATA = 3
#CSR_BUTTONS = 4
#CSR_BUTTONBITS = 5
#CSR_BTNNAMES = 6
#CSR_BUTTONMAP = 7
#CSR_SYSBTNMAP = 8
#CSR_NPBUTTON = 9
#CSR_NPBTNMARKS = 10
#CSR_NPRESPONSE = 11
#CSR_TPBUTTON = 12
#CSR_TPBTNMARKS = 13
#CSR_TPRESPONSE = 14
#CSR_PHYSID = 15
#CSR_MODE = 16
#CSR_MINPKTDATA = 17
#CSR_MINBUTTONS = 18
#CSR_CAPABILITIES = 19
#CSR_MAX = 19
;     
; wti_extensions (t7.9)
;
#EXT_NAME = 1
#EXT_TAG = 2
#EXT_MASK = 3
#EXT_SIZE = 4
#EXT_AXES = 5
#EXT_DEFAULT = 6
#EXT_DEFCONTEXT = 7
#EXT_DEFSYSCTX = 8
#EXT_CURSORS = 9
#EXT_MAX = 109        
;     
; system button assignment values (t7.10)
;
#SBN_NONE = $0
#SBN_LCLICK = $1
#SBN_LDBLCLICK = $2
#SBN_LDRAG = $3
#SBN_RCLICK = $4
#SBN_RDBLCLICK = $5
#SBN_RDRAG = $6
#SBN_MCLICK = $7
#SBN_MDBLCLICK = $8
#SBN_MDRAG = $9
;       
; pen windows assignments (t7.10)
;
#SBN_PTCLICK = $10
#SBN_PTDBLCLICK = $20
#SBN_PTDRAG = $30
#SBN_PNCLICK = $40
#SBN_PNDBLCLICK = $50
#SBN_PNDRAG = $60
#SBN_P1CLICK = $70
#SBN_P1DBLCLICK = $80
#SBN_P1DRAG = $90
#SBN_P2CLICK = $A0
#SBN_P2DBLCLICK = $B0
#SBN_P2DRAG = $C0
#SBN_P3CLICK = $D0
#SBN_P3DBLCLICK = $E0
#SBN_P3DRAG = $F0
;
; context option values (t7.11)
;
#CXO_SYSTEM = $1
#CXO_PEN = $2
#CXO_MESSAGES = $4
#CXO_MARGIN = $8000
#CXO_MGNINSIDE = $4000
;     
; context status values (t.12)
;
#CXS_DISABLED = $1
#CXS_OBSCURED = $2
#CXS_ONTOP = $4
;     
; context lock condition values (t7.13)
;
#CXL_INSIZE = $1
#CXL_INASPECT = $2
#CXL_SENSITIVITY = $4
#CXL_MARGIN = $8
#CXL_SYSOUT = $10
;     
; relative buttons (t7.13)
;
#TBN_NONE = 0
#TBN_UP = 1
#TBN_DOWN = 2
;
; packet status values (t7.14)
;
#TPS_PROXIMITY = $1
#TPS_QUEUE_ERR = $2
#TPS_MARGIN = $4
#TPS_GRAB = $8
#TPS_INVERT = $10
;     
; hook constants
;
#WTH_PLAYBACK = 1
#WTH_RECORD = 2
#WTHC_GETLPLPFN = (-3)
#WTHC_LPLPFNNEXT = (-2)
#WTHC_LPFNNEXT = (-1)
#WTHC_ACTION = 0
#WTHC_GETNEXT = 1
#WTHC_SKIP = 2 
;       
; cursor capabilities
;
#CRC_MULTIMODE = $1
#CRC_AGGREGATE = $2
#CRC_INVERT = $4
;    
Structure tagLOGCONTEXT           ; (ch7.3.1 p47)
  lcName.b[40]                    ; name
  lcOptions.l                     ; options (see t7.11)
  lcStatus.l                      ; current status (see t7.12)
  lcLocks.l                       ; locked conditions (see t7.13)
  lcMsgBase.l                     ; range of message numbers (see ch6)
  lcDevice.l                      ; device
  lcPktRate.l                     ; packet report rate in hz (n per sec)
  lcPktData.l                     ; optional data to report (?)
  lcPktMode.l                     ; bit per option, 1 = relative, 0 = absolute
  lcMoveMask.l                    ; specify packet data items that generate move events
  lcBtnDnMask.l                   ; specify buttons that generate button events
  lcBtnUpMask.l                   ; (p48)
  lcInOrgX.l                      ; xyz origin on input (tablet)
  lcInOrgY.l
  lcInOrgZ.l
  lcInExtX.l                      ; xyz range on input (tablet)
  lcInExtY.l
  lcInExtZ.l
  lcOutOrgX.l                     ; xyz origin on output (dc?)
  lcOutOrgY.l
  lcOutOrgZ.l
  lcOutExtX.l                     ; xyz range on output
  lcOutExtY.l
  lcOutExtZ.l
  lcSensX.l                       ; sensitivity in relative mode
  lcSensY.l
  lcSensZ.l
  lcSysMode.l                     ; system cursor tracking mode (0 = absolute) (p49)
  lcSysOrgX.l                     ; origin of screen mapping are for system cursor tracking in screen coordinates
  lcSysOrgY.l
  lcSysExtX.l
  lcSysExtY.l
  lcSysSensX.l                    ; system cursor relative mode sensitivity
  lcSysSensY.l
EndStructure
;
Structure tagAXIS             
  axMin.l
  axMax.l
  axUnits.l
  axResolution.l                  ; fix32, float = fix32 / 65536
EndStructure
;
;
; enough declarations that are not used... now, let's open a window and do some stuff
;
main_nr.l = x_nr()
main_whnd.l = OpenWindow(main_nr,300,350,400,200,#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget,"Test")
;
wt_nr.l = x_nr()
OpenLibrary(wt_nr,"WINTAB32.DLL")
;
;
; the following is nice if you want to list all the stuff in a library
; (has nothing to do with this example though :-))
;
;   Debug ExamineLibraryFunctions(wt_nr)
;   While NextLibraryFunction()<>0
;     Debug LibraryFunctionName()
;     Debug LibraryFunctionAddress()
;   Wend
;
;
; find functions in the wintab32 dll (there's a lot more but i haven't declared all)
;
WTInfo = IsFunction(wt_nr,"WTInfoA")
WTOpen = IsFunction(wt_nr,"WTOpenA")
WTPacketsGet = IsFunction(wt_nr,"WTPacketsGet")
WTClose = IsFunction(wt_nr,"WTClose")
;
; get size of the largest possible reply from WTInfo, can also be used to check existence of a tablet
;
;   buffer_l.l = CallFunctionFast(WTInfo,0,0,0)
;
; set up a context for wt_WTOpen
;
; notes:
;
;   still gotta' play a little with this, there are some interesting things possible
;   and some things don't behave as i would expect :-(
;   the context is not automatically updated after screen resizes
;   as far as i can tell, this tablet / driver only supports ONE context at a time
;   opening up multiple programs with tablet support does not go well
;   setting up coords / corner points in the LOGCONTEXT might solve this if all programs do so (fat chance)
;
tablet_lc.tagLOGCONTEXT                                           ; create context
;
; CallFunctionFast(WTInfo,#WTI_DEFCONTEXT,0,@tablet_lc)           ; fill it with default values
CallFunctionFast(WTInfo,#WTI_DEFSYSCTX,0,@tablet_lc)              ; fill it with default values
;
PokeS(@tablet_lc\lcName,"TEST_TABLET",39)
tablet_lc\lcOptions = tablet_lc\lcOptions | #CXO_MESSAGES         ; options (see t7.11)
tablet_lc\lcMsgBase = #WT_DEFBASE                                 ; base for message numbers (see ch6)
tablet_lc\lcPktData = #PK_X|#PK_Y|#PK_NORMAL_PRESSURE|#PK_BUTTONS ; data to report 
tablet_lc\lcPktMode = 0                                           ; bit per option, 1 = relative, 0 = absolute
tablet_lc\lcMoveMask = tablet_lc\lcPktData                        ; specify packet data items that generate move events
tablet_lc\lcBtnDnMask = #SBN_LCLICK|#SBN_RCLICK|#SBN_LDBLCLICK    ; specify buttons that generate button events
tablet_lc\lcBtnUpMask = tablet_lc\lcBtnDnMask                     ; (p48)
tablet_hctx = CallFunctionFast(WTOpen,main_whnd,@tablet_lc,1)     ; open and enable it
;
Structure WT_PACKET            ; (see p52) size and length of and fields in packet struct depend on options set
  pkButtons.l
  pkX.l
  pkY.l
  pkNormalPressure.l
EndStructure
;
packet.WT_PACKET
;
mouselmb.l = #False
done.l = #False
Repeat
  ;
  ; processing windows messages using waitevent() instead of callback
  ;
  event.l = WaitWindowEvent()                   ; wait for an event
  window.l = EventWindowID()                    ; on which window?
  ;
  Select event
    Case #WM_KEYDOWN 
      Debug "256 #WM_KEYDOWN key pressed"
      event_parameter.l = EventwParam()         ; undocumented / deprecated / windows only
      Select event_parameter                    ; eventwparam() gets additional info on event
        Case #VK_ESCAPE
          done=#True
      EndSelect
    Case #PB_Event_CloseWindow
      ; Debug "16 #PB_Event_CloseWindow close window"
      done = #True  
    Case #WM_MOUSEMOVE
      ; Debug "512 $200 #WM_MOUSEMOVE mouse moved over window"
      ;
      ; EventwParam() contains info on variuos virtual keys
      ; EventlParam() contains the cursor coords
      ;
      ; the implementation of mousecoordinates is somewhat fuzzy in purebasic, they are
      ; reported in relation to the upper left corner of the window, not of the client area
      ; using the EventlParam() field an accurate position can be retrieved
      ;
      mousex.l = EventlParam() % 65536
      mousey.l = EventlParam() / 65536
      ;
      ; if you want to use the build in commands, you have to compensate for the size 
      ; of window borders etc.
      ;
      ;   mousex.l = WindowMouseX()-GetSystemMetrics_(#SM_CYSIZEFRAME)
      ;   mousey.l = WindowMouseY()-GetSystemMetrics_(#SM_CYCAPTION)-GetSystemMetrics_(#SM_CYSIZEFRAME)
      ;      
      ; if you want To detect when the mouse leaves the window, you could set a TrackMouseEvent_()
      ;
      ;   mouseleave.tagTRACKMOUSEEVENT
      ;   mouseleave\cbSize = SizeOf(tagTRACKMOUSEEVENT)
      ;   mouseleave\dwFlags = #TME_LEAVE
      ;   mouseleave\hwndTrack = main_whnd
      ;   TrackMouseEvent_(@mouseleave)      
      ;
      ; when using SetCapture_() windows 'prefilters' messages, that is you won't receive
      ; messages unless another condition is met (virtual keys, mousebuttons, etc.)
      ; so you can't use SetCapture() to see if the cursor left the area with no button
      ; pressed
      ;
    Case #WM_LBUTTONDOWN
      ;
      ; Debug "513 $201 #WM_LBUTTONDOWN lmb down"
      ;
      mouselmb = #True
      ;
      ; if a mousebutton is pressed, and the cursor is moved outside the window client area
      ; no more messages will be received, including the WM_LBUTTONUP message
      ; to make sure such a message is received grab all messages until we got what we
      ; want
      ;
      SetCapture_(main_whnd)
      ;
    Case #WM_LBUTTONUP
      ;
      ; Debug "514 $202 #WM_LBUTTONUP lmb up"
      ;
      mouselmb = #False
      ;
      ReleaseCapture_()
      ;
    Case #WM_MOUSELEAVE
      ;
      Debug "675 $2A3WM_MOUSELEAVE mouse left window (after a trackmouseevent was used)"
      ;  
      ; only generated after calling TrackMouseEvent_(), see #WM_MOUSEMOVE above
      ;
    Default
      ; 
      ; all other events
      ;
      If event >= #WT_DEFBASE And event <= #WT_MAX
        CallFunctionFast(WTPacketsGet,tablet_hctx,1,@packet)
        ;
        ; there's more information that can be retrieved from the tablet, depending on type etc.
        ; here i only look for pressure
        ;
        pressure.l = packet\pkNormalPressure
      EndIf
  EndSelect
  ;  
  ; only draw if something has changed
  ; moves a circle over the window, size depending on pressure, erased when moving
  ;
  If mousex<>mousex_old Or mousey<>mousey_old Or pressure <> pressure_old
    StartDrawing(WindowOutput())
    DrawingMode(2)
    If pressure_old > 0                                             ; erase old circle
      Circle(mousex_old,mousey_old,pressure_old/10,RGB(0,0,0))
    EndIf  
    If pressure > 0                                                 ; tablet was used
      Circle(mousex,mousey,pressure/10,RGB(0,0,0))
    EndIf
    mousex_old = mousex
    mousey_old = mousey
    pressure_old = pressure
    StopDrawing()
  EndIf
  ;
Until done = #True
;
CallFunctionFast(WTClose,tablet_hctx)                      ; close and destroy
CloseLibrary(wintab_nr)
;
x_hidedebugger()
x_end()


Posted: Wed Nov 12, 2003 12:44 am
by Karbon
Very nice!

Posted: Wed Nov 12, 2003 8:16 pm
by Skipsy
Oups...

Problem when compiling...
It says "Only a litteral string can be put after'includefile' "
at line : IncludeFile("x_lib.pb")

Where am I supposed to find "x_lib.pb" ?

WW

Posted: Wed Nov 12, 2003 8:52 pm
by blueznl
oooooooops

well, you can take it from the survival guide (it's part of the zip file on my page), or take all calls to the lib out

i only use x_nr iirc, and that function just returns some unique numbers

Posted: Thu Nov 13, 2003 10:57 am
by Skipsy
Hi,

I have had to download PB 3.8 upgrade.
Now compiling is OK but application crashes :
"PureBasic6992985 has encounter a problem and must close"
" Do you want to send report to Microsoft bla, bla..."

I have also tried with debug ON... same message.... sorry

Tested on Dell C640 Windows XP SP1 256 MO ram

[quote]I've just discovered this and I like it's potential[/

Posted: Sun Sep 12, 2004 3:03 am
by marvin
[/quote] ;
; only draw if something has changed
; moves a circle over the window, size depending on pressure, erased when moving
;
;If mousex<>mousex_old Or mousey<>mousey_old Or pressure <> pressure_old
;SetFrameRate(100)
StartDrawing(WindowOutput())

;Changed this
DrawingMode(1)
;If pressure_old > 0 ; erase old circle
;Circle(mousex_old+mousex,mousey_old+mousey,pressure_old/10,RGB(0,0,0))
;EndIf
;If pressure > 0 ; tablet was used
Circle(mousex,mousey,pressure/60,RGB(255,255,0))
;EndIf
;mousex_old = mousex
;mousey_old = mousey
;pressure_old = pressure
;StopDrawing()
;EndIf

I changed the drawing mode and made it able to leave a drawing trail.
I need help in making a continous line with it. Like what it is suppose to do, instead I only got it to draw dots that don't connect when I move the pen too fast.

Can someone help me here?

Thanks

Posted: Sun Oct 03, 2004 9:00 am
by blueznl
did you ever get it working?

Posted: Wed Oct 06, 2004 9:22 am
by marvin
blueznl, not sure if this question is for me but, If you are asking me, the answer is yes! I searched and found this procedure. Added it to your code to get a continuous line along with pressure line thickness control.

Procedure Lin(x,y,x1,y1,Gros,Color)
hDC=GetDC_(WindowID())
pen=CreatePen_(#Ps_Solid,GROS,color)
hPenOld=SelectObject_(hDC,pen)
MoveToEx_(hDC,x,y,0):LineTo_(hDC,x1,y1)
DeleteObject_(pen)
DeleteObject_(hPenOld)
EndProcedure
It was implemented here.
If mousex<>mousex_old Or mousey<>mousey_old Or pressure <> pressure_old
;SetFrameRate(100)
StartDrawing(WindowOutput())
DrawingMode(0)

If pressure > 0 ; tablet was used

lin(mousex_old,mousey_old,mousex,mousey,pressure/30,RGB(Random(25),Random(5),#red))

EndIf
mousex_old = mousex
mousey_old = mousey
pressure_old = pressure
StopDrawing()
EndIf
haven't yet work out the bugs but it works like intended.

Thanks bluenl, even if you weren't pertaining to my issue.

Posted: Wed Oct 06, 2004 3:20 pm
by blueznl
no prob, it was indeed your message i was refering to

Re: touch tablet

Posted: Sat Jun 05, 2010 10:42 am
by zxtunes.com
Update for PB 4.5 and remove x_lib , i tested this, it work fine.
Thank you blueznl!! :D

WINTAB32.dll: http://zenratai.com/software/stereopain ... NTAB32.dll

Code: Select all

; purebasic survival guide
; touch_tablet - 11.11.2003 ejn (blueznl)
; http://www.xs4all.nl/~bluez/datatalk/pure1.htm
;
; - using a touch tablet in purebasic
; - references to pages / tables refer to a word document by lcs/telegraphics:
;
;     wintab interface specification 1.0
;     16- and 32-bit api reference by Rick Poyner
;     revised december 13, 1994
;
; - use google, the doc above is NOT part of the wintab sdk (duh!) but is on their site
; - to do: disable tablet context when window is not in focus
; - to do: handle pen vs. lmb issues (pen can generate lmb message at pressure 0, timing is all...)
; - to do: tilt detection (wacom) etc.
;
;
;

;
; wanna' have a message to notify the mouse leaving the window? use the trackmouseevent_() function
; here's the structure and the constants we might need later (uncomment when needed)
;
;   Structure tagTRACKMOUSEEVENT
;     cbSize.l
;     dwFlags.l
;     hwndTrack.l
;     dwHoverTime.l
;   EndStructure
;   #TME_LEAVE = 2
;
#WM_MOUSELEAVE = $2A3
;
;
; *** wintab constants and structures
;
; device configuration constants (ch5.7.1 p21)
;
#WTDC_NONE = 0
#WTDC_CANCEL = 1
#WTDC_OK = 2
#WTDC_RESTART = 3
;     
; message constants (ch6 p34)
;
#WT_DEFBASE = $7FF0
#WT_MAXOFFSET = $F
#WT_MAX = $7FFF                              ; aka wt_defbase + wt_maxoffset
Enumeration #WT_DEFBASE
  #WT_PACKET
  #WT_CTXOPEN
  #WT_CTXCLOSE
  #WT_CTXUPDATE
  #WT_CTXOVERLAP
  #WT_PROXIMITY
  #WT_INFOCHANGE
  #WT_CSRCHANGE
EndEnumeration
; 
; packet constants (t7.1 p38)
;
#PK_CONTEXT = $1                            ; context
#PK_STATUS = $2                             ; status bits
#PK_TIME = $4                               ; time stamp
#PK_CHANGED = $8                            ; change bit vector
#PK_SERIAL_NUMBER = $10                     ; packet serial number
#PK_CURSOR = $20                            ; reporting cursor
#PK_BUTTONS = $40                           ; button information
#PK_X = $80                                 ; x-axis
#PK_Y = $100                                ; y-axis
#PK_Z = $200                                ; z-axis
#PK_NORMAL_PRESSURE = $400                  ; normal or tip pressure
#PK_TANGENT_PRESSURE = $800                 ; tangential or barrel pressure
#PK_ORIENTATION = $1000                     ; orientation info / tilts
#PK_ROTATION = $2000       
;
; unit specifiers (t7.2)
;
#TU_NONE = 0
#TU_INCHES = 1
#TU_CENTIMETERS = 2
#TU_CIRCLE = 3
;
; wti category definitions (t7.3)
;
#WTI_INTERFACE = 1
#WTI_STATUS = 2
#WTI_DEFCONTEXT = 3
#WTI_DEFSYSCTX = 4
#WTI_DEVICES = 100
#WTI_CURSORS = 200
#WTI_EXTENSIONS = 300
#WTI_DDCTXS = 400
#WTI_DSCTXS = 500
;
; wti_interface index definitions (t7.4)
;
#IFC_WINTABID = 1
#IFC_SPECVERSION = 2
#IFC_IMPLVERSION = 3
#IFC_NDEVICES = 4
#IFC_NCURSORS = 5
#IFC_NCONTEXTS = 6
#IFC_CTXOPTIONS = 7
#IFC_CTXSAVESIZE = 8
#IFC_NEXTENSIONS = 9
#IFC_NMANAGERS = 10
#IFC_MAX = 10
;
; wti_status index definitions (t7.5)
;
#STA_CONTEXTS = 1
#STA_SYSCTXS = 2
#STA_PKTRATE = 3
#STA_PKTDATA = 4
#STA_MANAGERS = 5
#STA_SYSTEM = 6
#STA_BUTTONUSE = 7
#STA_SYSBTNUSE = 8
#STA_MAX = 8
;
; wti_defcontext / wti_defsysctx index definitions (t7.6)
;
#CTX_NAME = 1
#CTX_OPTIONS = 2
#CTX_STATUS = 3
#CTX_LOCKS = 4
#CTX_MSGBASE = 5
#CTX_DEVICE = 6
#CTX_PKTRATE = 7
#CTX_PKTDATA = 8
#CTX_PKTMODE = 9
#CTX_MOVEMASK = 10
#CTX_BTNDNMASK = 11
#CTX_BTNUPMASK = 12
#CTX_INORGX = 13
#CTX_INORGY = 14
#CTX_INORGZ = 15
#CTX_INEXTX = 16
#CTX_INEXTY = 17
#CTX_INEXTZ = 18
#CTX_OUTORGX = 19
#CTX_OUTORGY = 20
#CTX_OUTORGZ = 21
#CTX_OUTEXTX = 22
#CTX_OUTEXTY = 23
#CTX_OUTEXTZ = 24
#CTX_SENSX = 25
#CTX_SENSY = 26
#CTX_SENSZ = 27
#CTX_SYSMODE = 28
#CTX_SYSORGX = 29
#CTX_SYSORGY = 30
#CTX_SYSEXTX = 31
#CTX_SYSEXTY = 32
#CTX_SYSSENSX = 33
#CTX_SYSSENSY = 34
#CTX_MAX = 34
;     
; wti_devices index definitions (t7.7)
;
#DVC_NAME = 1
#DVC_HARDWARE = 2
#DVC_NCSRTYPES = 3
#DVC_FIRSTCSR = 4
#DVC_PKTRATE = 5
#DVC_PKTDATA = 6
#DVC_PKTMODE = 7
#DVC_CSRDATA = 8
#DVC_XMARGIN = 9
#DVC_YMARGIN = 10
#DVC_ZMARGIN = 11
#DVC_X = 12
#DVC_Y = 13
#DVC_Z = 14
#DVC_NPRESSURE = 15
#DVC_TPRESSURE = 16
#DVC_ORIENTATION = 17
#DVC_ROTATION = 18
#DVC_PNPID = 19
#DVC_MAX = 19
;     
; dvc_hardware hardware capabilities (t7.7)
;
#HWC_INTEGRATED = $1
#HWC_TOUCH = $2
#HWC_HARDPROX = $4
#HWC_PHYSID_CURSORS = $8
;       
; wti_cursors (t7.8)
;
#CSR_NAME = 1
#CSR_ACTIVE = 2
#CSR_PKTDATA = 3
#CSR_BUTTONS = 4
#CSR_BUTTONBITS = 5
#CSR_BTNNAMES = 6
#CSR_BUTTONMAP = 7
#CSR_SYSBTNMAP = 8
#CSR_NPBUTTON = 9
#CSR_NPBTNMARKS = 10
#CSR_NPRESPONSE = 11
#CSR_TPBUTTON = 12
#CSR_TPBTNMARKS = 13
#CSR_TPRESPONSE = 14
#CSR_PHYSID = 15
#CSR_MODE = 16
#CSR_MINPKTDATA = 17
#CSR_MINBUTTONS = 18
#CSR_CAPABILITIES = 19
#CSR_MAX = 19
;     
; wti_extensions (t7.9)
;
#EXT_NAME = 1
#EXT_TAG = 2
#EXT_MASK = 3
#EXT_SIZE = 4
#EXT_AXES = 5
#EXT_DEFAULT = 6
#EXT_DEFCONTEXT = 7
#EXT_DEFSYSCTX = 8
#EXT_CURSORS = 9
#EXT_MAX = 109       
;     
; system button assignment values (t7.10)
;
#SBN_NONE = $0
#SBN_LCLICK = $1
#SBN_LDBLCLICK = $2
#SBN_LDRAG = $3
#SBN_RCLICK = $4
#SBN_RDBLCLICK = $5
#SBN_RDRAG = $6
#SBN_MCLICK = $7
#SBN_MDBLCLICK = $8
#SBN_MDRAG = $9
;       
; pen windows assignments (t7.10)
;
#SBN_PTCLICK = $10
#SBN_PTDBLCLICK = $20
#SBN_PTDRAG = $30
#SBN_PNCLICK = $40
#SBN_PNDBLCLICK = $50
#SBN_PNDRAG = $60
#SBN_P1CLICK = $70
#SBN_P1DBLCLICK = $80
#SBN_P1DRAG = $90
#SBN_P2CLICK = $A0
#SBN_P2DBLCLICK = $B0
#SBN_P2DRAG = $C0
#SBN_P3CLICK = $D0
#SBN_P3DBLCLICK = $E0
#SBN_P3DRAG = $F0
;
; context option values (t7.11)
;
#CXO_SYSTEM = $1
#CXO_PEN = $2
#CXO_MESSAGES = $4
#CXO_MARGIN = $8000
#CXO_MGNINSIDE = $4000
;     
; context status values (t.12)
;
#CXS_DISABLED = $1
#CXS_OBSCURED = $2
#CXS_ONTOP = $4
;     
; context lock condition values (t7.13)
;
#CXL_INSIZE = $1
#CXL_INASPECT = $2
#CXL_SENSITIVITY = $4
#CXL_MARGIN = $8
#CXL_SYSOUT = $10
;     
; relative buttons (t7.13)
;
#TBN_NONE = 0
#TBN_UP = 1
#TBN_DOWN = 2
;
; packet status values (t7.14)
;
#TPS_PROXIMITY = $1
#TPS_QUEUE_ERR = $2
#TPS_MARGIN = $4
#TPS_GRAB = $8
#TPS_INVERT = $10
;     
; hook constants
;
#WTH_PLAYBACK = 1
#WTH_RECORD = 2
#WTHC_GETLPLPFN = (-3)
#WTHC_LPLPFNNEXT = (-2)
#WTHC_LPFNNEXT = (-1)
#WTHC_ACTION = 0
#WTHC_GETNEXT = 1
#WTHC_SKIP = 2
;       
; cursor capabilities
;
#CRC_MULTIMODE = $1
#CRC_AGGREGATE = $2
#CRC_INVERT = $4
;   
Structure tagLOGCONTEXT           ; (ch7.3.1 p47)
  lcName.b[40]                    ; name
  lcOptions.l                     ; options (see t7.11)
  lcStatus.l                      ; current status (see t7.12)
  lcLocks.l                       ; locked conditions (see t7.13)
  lcMsgBase.l                     ; range of message numbers (see ch6)
  lcDevice.l                      ; device
  lcPktRate.l                     ; packet report rate in hz (n per sec)
  lcPktData.l                     ; optional data to report (?)
  lcPktMode.l                     ; bit per option, 1 = relative, 0 = absolute
  lcMoveMask.l                    ; specify packet data items that generate move events
  lcBtnDnMask.l                   ; specify buttons that generate button events
  lcBtnUpMask.l                   ; (p48)
  lcInOrgX.l                      ; xyz origin on input (tablet)
  lcInOrgY.l
  lcInOrgZ.l
  lcInExtX.l                      ; xyz range on input (tablet)
  lcInExtY.l
  lcInExtZ.l
  lcOutOrgX.l                     ; xyz origin on output (dc?)
  lcOutOrgY.l
  lcOutOrgZ.l
  lcOutExtX.l                     ; xyz range on output
  lcOutExtY.l
  lcOutExtZ.l
  lcSensX.l                       ; sensitivity in relative mode
  lcSensY.l
  lcSensZ.l
  lcSysMode.l                     ; system cursor tracking mode (0 = absolute) (p49)
  lcSysOrgX.l                     ; origin of screen mapping are for system cursor tracking in screen coordinates
  lcSysOrgY.l
  lcSysExtX.l
  lcSysExtY.l
  lcSysSensX.l                    ; system cursor relative mode sensitivity
  lcSysSensY.l
EndStructure
;
Structure tagAXIS             
  axMin.l
  axMax.l
  axUnits.l
  axResolution.l                  ; fix32, float = fix32 / 65536
EndStructure
;
;
; enough declarations that are not used... now, let's open a window and do some stuff
;
main_nr.l = 777
main_whnd.l = OpenWindow(main_nr,300,350,400,200,"Test", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget)
;
wt_nr.l = 778
OpenLibrary(wt_nr,"WINTAB32.dll")
;
;
; the following is nice if you want to list all the stuff in a library
; (has nothing to do with this example though :-))
;
;   Debug ExamineLibraryFunctions(wt_nr)
;   While NextLibraryFunction()<>0
;     Debug LibraryFunctionName()
;     Debug LibraryFunctionAddress()
;   Wend
;
;
; find functions in the wintab32 dll (there's a lot more but i haven't declared all)
;
WTInfo = GetFunction(wt_nr,"WTInfoA")
WTOpen = GetFunction(wt_nr,"WTOpenA")
WTPacketsGet = GetFunction(wt_nr,"WTPacketsGet")
WTClose = GetFunction(wt_nr,"WTClose")
;
; get size of the largest possible reply from WTInfo, can also be used to check existence of a tablet
;
;   buffer_l.l = CallFunctionFast(WTInfo,0,0,0)
;
; set up a context for wt_WTOpen
;
; notes:
;
;   still gotta' play a little with this, there are some interesting things possible
;   and some things don't behave as i would expect :-(
;   the context is not automatically updated after screen resizes
;   as far as i can tell, this tablet / driver only supports ONE context at a time
;   opening up multiple programs with tablet support does not go well
;   setting up coords / corner points in the LOGCONTEXT might solve this if all programs do so (fat chance)
;
tablet_lc.tagLOGCONTEXT                                           ; create context
;
; CallFunctionFast(WTInfo,#WTI_DEFCONTEXT,0,@tablet_lc)           ; fill it with default values
CallFunctionFast(WTInfo,#WTI_DEFSYSCTX,0,@tablet_lc)              ; fill it with default values
;
PokeS(@tablet_lc\lcName,"TEST_TABLET",39)
tablet_lc\lcOptions = tablet_lc\lcOptions | #CXO_MESSAGES         ; options (see t7.11)
tablet_lc\lcMsgBase = #WT_DEFBASE                                 ; base for message numbers (see ch6)
tablet_lc\lcPktData = #PK_X|#PK_Y|#PK_NORMAL_PRESSURE|#PK_BUTTONS ; data to report
tablet_lc\lcPktMode = 0                                           ; bit per option, 1 = relative, 0 = absolute
tablet_lc\lcMoveMask = tablet_lc\lcPktData                        ; specify packet data items that generate move events
tablet_lc\lcBtnDnMask = #SBN_LCLICK|#SBN_RCLICK|#SBN_LDBLCLICK    ; specify buttons that generate button events
tablet_lc\lcBtnUpMask = tablet_lc\lcBtnDnMask                     ; (p48)
tablet_hctx = CallFunctionFast(WTOpen,main_whnd,@tablet_lc,1)     ; open and enable it
;
Structure WT_PACKET            ; (see p52) size and length of and fields in packet struct depend on options set
  pkButtons.l
  pkX.l
  pkY.l
  pkNormalPressure.l
EndStructure
;
packet.WT_PACKET
;
mouselmb.l = #False
done.l = #False
Repeat
  ;
  ; processing windows messages using waitevent() instead of callback
  ;
  Event.l = WaitWindowEvent()                   ; wait for an event
  Window.l = EventWindow()                    ; on which window?
  ;
  Select Event
    Case #WM_KEYDOWN
      Debug "256 #WM_KEYDOWN key pressed"
      event_parameter.l = EventwParam()         ; undocumented / deprecated / windows only
      Select event_parameter                    ; eventwparam() gets additional info on event
        Case #VK_ESCAPE
          done=#True
      EndSelect
    Case #PB_Event_CloseWindow
      ; Debug "16 #PB_Event_CloseWindow close window"
      done = #True 
    Case #WM_MOUSEMOVE
      ; Debug "512 $200 #WM_MOUSEMOVE mouse moved over window"
      ;
      ; EventwParam() contains info on variuos virtual keys
      ; EventlParam() contains the cursor coords
      ;
      ; the implementation of mousecoordinates is somewhat fuzzy in purebasic, they are
      ; reported in relation to the upper left corner of the window, not of the client area
      ; using the EventlParam() field an accurate position can be retrieved
      ;
      mousex.l = EventlParam() % 65536
      mousey.l = EventlParam() / 65536
      ;
      ; if you want to use the build in commands, you have to compensate for the size
      ; of window borders etc.
      ;
      ;   mousex.l = WindowMouseX()-GetSystemMetrics_(#SM_CYSIZEFRAME)
      ;   mousey.l = WindowMouseY()-GetSystemMetrics_(#SM_CYCAPTION)-GetSystemMetrics_(#SM_CYSIZEFRAME)
      ;     
      ; if you want To detect when the mouse leaves the window, you could set a TrackMouseEvent_()
      ;
      ;   mouseleave.tagTRACKMOUSEEVENT
      ;   mouseleave\cbSize = SizeOf(tagTRACKMOUSEEVENT)
      ;   mouseleave\dwFlags = #TME_LEAVE
      ;   mouseleave\hwndTrack = main_whnd
      ;   TrackMouseEvent_(@mouseleave)     
      ;
      ; when using SetCapture_() windows 'prefilters' messages, that is you won't receive
      ; messages unless another condition is met (virtual keys, mousebuttons, etc.)
      ; so you can't use SetCapture() to see if the cursor left the area with no button
      ; pressed
      ;
    Case #WM_LBUTTONDOWN
      ;
      ; Debug "513 $201 #WM_LBUTTONDOWN lmb down"
      ;
      mouselmb = #True
      ;
      ; if a mousebutton is pressed, and the cursor is moved outside the window client area
      ; no more messages will be received, including the WM_LBUTTONUP message
      ; to make sure such a message is received grab all messages until we got what we
      ; want
      ;
      SetCapture_(main_whnd)
      ;
    Case #WM_LBUTTONUP
      ;
      ; Debug "514 $202 #WM_LBUTTONUP lmb up"
      ;
      mouselmb = #False
      ;
      ReleaseCapture_()
      ;
    Case #WM_MOUSELEAVE
      ;
      Debug "675 $2A3WM_MOUSELEAVE mouse left window (after a trackmouseevent was used)"
      ; 
      ; only generated after calling TrackMouseEvent_(), see #WM_MOUSEMOVE above
      ;
    Default
      ;
      ; all other events
      ;
      If Event >= #WT_DEFBASE And Event <= #WT_MAX
        CallFunctionFast(WTPacketsGet,tablet_hctx,1,@packet)
        ;
        ; there's more information that can be retrieved from the tablet, depending on type etc.
        ; here i only look for pressure
        ;
        pressure.l = packet\pkNormalPressure
      EndIf
  EndSelect
  ; 
  ; only draw if something has changed
  ; moves a circle over the window, size depending on pressure, erased when moving
  ;
  If mousex<>mousex_old Or mousey<>mousey_old Or pressure <> pressure_old
    StartDrawing(WindowOutput(main_nr))
      DrawingMode(2)
      If pressure_old > 0                                             ; erase old circle
        Circle(mousex_old,mousey_old,pressure_old/10,RGB(0,0,0))
      EndIf 
      If pressure > 0                                                 ; tablet was used
        Circle(mousex,mousey,pressure/10,RGB(0,0,0))
      EndIf
      mousex_old = mousex
      mousey_old = mousey
      pressure_old = pressure
    StopDrawing()
  EndIf
  ;
Until done = #True
;
CallFunctionFast(WTClose,tablet_hctx)                      ; close and destroy
CloseLibrary(wintab_nr)