>* >* Tablet >* >* A utility class to assist in VFP tablet programming >* >* Properties: >* IsTablet - .T. if PC is a tablet >* TabletMode - .T. if running in tablet mode >* Orientation - "L" for landscape, "P" for portrait >* >* Methods: >* IsTabletModeOn() - .T. if running in tablet mode >* >* Events: >* OnTabletModeChange() - when user changes modes >* OnOrientationChange() - when user changes orientation >* > >* Windows message identifiers >#DEFINE WM_SETTINGCHANGE 0x001A >#DEFINE WM_DISPLAYCHANGE 0x007E > >* For registry inspection >#DEFINE HKEY_CURRENT_USER 0x80000001 >#DEFINE RRF_RT_DWORD 0x00000018 > >* System metrics keys >#DEFINE SM_CONVERTIBLESLATEMODE 0x2003 >#DEFINE SM_TABLETPC 86 >#DEFINE SM_CXFULLSCREEN 16 >#DEFINE SM_CYFULLSCREEN 17 > >* load the class definition >IF !SYS(16)$SET("Procedure") > SET PROCEDURE TO (SYS(16)) ADDITIVE >ENDIF > >DEFINE CLASS Tablet AS Custom > > HIDDEN OSVersion > > IsTablet = .F. > TabletMode = .F. > Orientation = "" > OSVersion = 0 > > _memberdata = '<VFPData>' + ; > '<memberdata name="istablet" type="property" display="IsTablet"/>' + ; > '<memberdata name="istabletmodeon" type="method" display="IsTabletModeOn"/>' + ; > '<memberdata name="onorientationchange" type="method" display="OnOrientationChange"/>' + ; > '<memberdata name="ontabletmodechange" type="method" display="OnTabletModeChange"/>' + ; > '<memberdata name="orientation" type="property" display="Orientation"/>' + ; > '<memberdata name="tabletmode" type="property" display="TabletMode"/>' + ; > '</VFPData>' > > FUNCTION Init > > LOCAL WMIService, OperatingSystem > > * declarations, we'll need these functions > DECLARE INTEGER GetSystemMetrics IN WIN32API INTEGER Metric > DECLARE INTEGER RegGetValue IN WIN32API ; > INTEGER hkey, STRING lpSubKey, STRING lpValue, INTEGER dwFlags, INTEGER @ dwType, STRING @ pvData, INTEGER @ pcbData > DECLARE Sleep IN WIN32API INTEGER Milliseconds > > * get the major OS version > m.WMIService = GETOBJECT("winmgmts:\\.\root\cimv2") > m.OperatingSystem = m.WMIService.InstancesOf("Win32_OperatingSystem").ItemIndex(0) > This.OSVersion = VAL(m.OperatingSystem.Version) > > * check if PC is a tablet > TRY > This.IsTablet = GetSystemMetrics(SM_TABLETPC) != 0 > CATCH > This.IsTablet = .F. > ENDTRY > > * set screen orientation > This.Orientation = IIF(GetSystemMetrics(SM_CXFULLSCREEN) > GetSystemMetrics(SM_CYFULLSCREEN), "L", "P") > > * set the tablet mode > This.IsTabletModeOn() > > * trap changes that may be broadcasted by Windows > BINDEVENT(_VFP.HWnd, WM_SETTINGCHANGE, This, "SystemChanged", 5) > BINDEVENT(_VFP.HWnd, WM_DISPLAYCHANGE, This, "SystemChanged", 5) > > ENDFUNC > > FUNCTION Destroy > > * forget about all our event bindings > UNBINDEVENTS(This) > > ENDFUNC > > * when relevant display settings are changed, Windows broadcasts messages to all [top-form] windows > * messages are trapped by this method that, in turn, fires the public event > HIDDEN PROCEDURE SystemChanged (hWnd AS Integer, WindowsMessage AS Integer, Param1 AS Integer, Param2 AS Integer) > > LOCAL P2 AS String > LOCAL Previous > LOCAL Waiting AS Integer > > DO CASE > CASE m.WindowsMessage = WM_DISPLAYCHANGE > > m.Previous = This.Orientation > > * new values for Screen WxH comes in Param2 > This.Orientation = IIF(m.Param2 % 0x10000 > INT(m.Param2 / 0x10000), "L", "P") > > * changed? > IF m.Previous != This.Orientation > > RAISEEVENT(This, "OnOrientationChange") > ENDIF > > CASE m.WindowsMessage = WM_SETTINGCHANGE > > * try to fetch a string from Param2 > TRY > m.P2 = STREXTRACT(SYS(2600, m.Param2, 255), "", CHR(0), 1, 2) > CATCH > m.P2 = "" > ENDTRY > > DO CASE > * pre-Windows 10 message for Tablet mode > CASE m.P2 == "ConvertibleSlateMode" > > IF This.OSVersion < 10 > m.Previous = This.TabletMode > > * check tablet mode > This.IsTabletModeOn() > > IF m.Previous != This.TabletMode > RAISEEVENT(This, "OnTabletModeChange") > ENDIF > > ENDIF > > * Windows 10 message for Tablet mode > CASE m.P2 == "UserInteractionMode" > > IF This.OSVersion >= 10 > > m.Previous = This.TabletMode > > This.IsTabletModeOn() > > m.Waiting = 250 > > DO WHILE m.Previous = This.TabletMode AND m.Waiting <= 2000 > * give some time to the system to do its work > Sleep(m.Waiting) > > m.Waiting = m.Waiting * 2 > > * check again > This.IsTabletModeOn() > ENDDO > > IF m.Previous != This.TabletMode > RAISEEVENT(This, "OnTabletModeChange") > ENDIF > ENDIF > ENDCASE > ENDCASE > > ENDPROC > > * verify if the laptop/device is in tablet mode > FUNCTION IsTabletModeOn > > * before Windows 10, get a system metric that holds the slate mode > IF This.OSVersion < 10 > > LOCAL SlateMode > > TRY > m.SlateMode = GetSystemMetrics(SM_CONVERTIBLESLATEMODE) > CATCH > m.SlateMode = 1 > ENDTRY > > This.TabletMode = EMPTY(m.SlateMode) > > * after Windows 10, inspect the registry > ELSE > > LOCAL BufferData AS String > LOCAL BufferLen AS Integer > LOCAL Zerro AS String > > * check for the registry value > m.BufferLen = 4 > m.Zero = REPLICATE(CHR(0), m.BufferLen) > m.BufferData = m.Zero > > RegGetValue(HKEY_CURRENT_USER, ; > "SOFTWARE\Microsoft\Windows\CurrentVersion\ImmersiveShell", ; > "TabletMode", ; > RRF_RT_DWORD, ; > 0, ; > @m.BufferData, ; > @m.BufferLen) > > This.TabletMode = m.BufferData != m.Zero > ENDIF > > RETURN This.TabletMode > > ENDPROC > > * events > PROCEDURE OnTabletModeChange > ENDPROC > > PROCEDURE OnOrientationChange > ENDPROC > >ENDDEFINE >>
>SET DEFAULT TO (JUSTPATH(SYS(16))) > >* put the class definition in scope >DO Tablet.prg > >* a simple error handler, just to help us debug this >ON ERROR DO ShowError WITH PROGRAM(), LINENO(), MESSAGE(1), MESSAGE(), ERROR() > >* the testers: a form and an extended instance of the tablet class, just to output something when there are events >LOCAL tester AS TestForm >LOCAL Controller AS TabletController > >m.Controller = CREATEOBJECT("TabletController") > >m.tester = CREATEOBJECT("TestForm") > >* set a reference to the form in a new property of the tablet class >m.Controller.OutputForm = m.tester > >* display the initial status >m.Controller.OnOrientationChange() >m.Controller.OnTabletModeChange() > >IF m.Controller.IsTablet > m.tester.Caption = "Testing in a tablet" > * if it is a tablet, let the user handle orientation change > m.tester.cmdChangeDisplayOrientation.Enabled = .F. >ELSE > m.tester.Caption = "Testing in a desktop / laptop" >ENDIF > >* show things as they happen >m.tester.Show() >READ EVENTS > >ON ERROR > >DEFINE CLASS TestForm AS Form > ADD OBJECT cmdChangeDisplayOrientation AS CommandButton WITH Top = 10, Left = 10, Caption = "Change Orientation", AutoSize = .T. > ADD OBJECT lblTabletMode AS Label WITH Top = 40, Left = 10, AutoSize = .T., Caption = "" > ADD OBJECT lblDisplayOrientation AS Label WITH Top = 70, Left = 10, AutoSize = .T., Caption = "" > > * how was the display orientated when we started > InitialOrientation = _Screen.DisplayOrientation > > * cycle throuh the different orientations (pressing the space bar will be ok) > PROCEDURE cmdChangeDisplayOrientation.Click > _Screen.DisplayOrientation = IIF(_Screen.DisplayOrientation = 3,0,_Screen.DisplayOrientation + 1) > ENDPROC > > PROCEDURE Destroy > * reset to the initial orientation > IF This.cmdChangeDisplayOrientation.Enabled AND _Screen.DisplayOrientation != This.InitialOrientation > _Screen.DisplayOrientation = This.InitialOrientation > ENDIF > CLEAR EVENTS > ENDPROC > >ENDDEFINE > >* extend the class tablet, to react to events (just display the different states) >DEFINE CLASS TabletController AS tablet > > OutputForm = .NULL. > > FUNCTION OnOrientationChange > > IF !ISNULL(This.OutputForm) > This.OutputForm.lblDisplayOrientation.Caption = ; > STREXTRACT("L:Landscape:P:Portrait", This.Orientation + ":",":",1,2) > ENDIF > > ENDFUNC > > FUNCTION OnTabletModeChange > IF !ISNULL(This.OutputForm) > This.OutputForm.lblTabletMode.Caption = IIF(This.TabletMode,"Tablet mode","Desktop mode") > ENDIF > ENDFUNC > >ENDDEFINE > >PROCEDURE ShowError (ProgramName, LineNumber, Source, ErrorText, ErrorNumber) > > MESSAGEBOX(m.ProgramName + ":" + TRANSFORM(m.LineNumber) + CHR(13) + ; > m.Source + CHR(13) + CHR(13) + ; > TRANSFORM(m.ErrorNumber) + ">" + m.ErrorText) > RETURN >ENDPROC >