; 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_lclcName,'TEST_TABLET',39) tablet_lclcOptions = tablet_lclcOptions | #CXO_MESSAGES ; options (see t7.11) tablet_lclcMsgBase = #WT_DEFBASE ; base for message numbers (see ch6) tablet_lclcPktData = #PK_X|#PK_Y|#PK_NORMAL_PRESSURE|#PK_BUTTONS ; data to report tablet_lclcPktMode = 0 ; bit per option, 1 = relative, 0 = absolute tablet_lclcMoveMask = tablet_lclcPktData ; specify packet data items that generate move events tablet_lclcBtnDnMask = #SBN_LCLICK|#SBN_RCLICK|#SBN_LDBLCLICK ; specify buttons that generate button events tablet_lclcBtnUpMask = tablet_lclcBtnDnMask ; (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 ; mouseleavecbSize = SizeOf(tagTRACKMOUSEEVENT) ; mouseleavedwFlags = #TME_LEAVE ; mouseleavehwndTrack = 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 = packetpkNormalPressure 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()
_________________ ( PB5.xx Win10 x64 Asrock AB350 Pro4 Ryzen 1600X 32GB RAM Evo 840 GTX1060 ) ( The path to enlightenment and the PureBasic Survival Guide right here... )
|