On this post I will show you, how you can change the screen orientation using Delphi. To do this task you must use 2 Winapi functions EnumDisplaySettings and ChangeDisplaySettings.
EnumDisplaySettings: this function retrieves information about the graphics modes supported by the display device.
for example to obtain the current display settings we must call this function in this way:
var dm : TDeviceMode; ZeroMemory(@dm, sizeof(dm)); dm.dmSize := sizeof(dm); if EnumDisplaySettings(nil, DWORD(ENUM_CURRENT_SETTINGS), dm) then begin //some code end;
Now , to use the ChangeDisplaySettings function to change the Screen Orientation we need to pass a valid DEVMODE structure, setting the value of the dmDisplayOrientation field, unfortunately the declaration in the Windows.pas for this record does not include this field.
this is the declaration for the DEVMODE structure in the Windows unit.
_devicemodeA = record dmDeviceName: array[0..CCHDEVICENAME - 1] of AnsiChar; dmSpecVersion: Word; dmDriverVersion: Word; dmSize: Word; dmDriverExtra: Word; dmFields: DWORD; dmOrientation: SHORT; dmPaperSize: SHORT; dmPaperLength: SHORT; dmPaperWidth: SHORT; dmScale: SHORT; dmCopies: SHORT; dmDefaultSource: SHORT; dmPrintQuality: SHORT; dmColor: SHORT; dmDuplex: SHORT; dmYResolution: SHORT; dmTTOption: SHORT; dmCollate: SHORT; dmFormName: array[0..CCHFORMNAME - 1] of AnsiChar; dmLogPixels: Word; dmBitsPerPel: DWORD; dmPelsWidth: DWORD; dmPelsHeight: DWORD; dmDisplayFlags: DWORD; dmDisplayFrequency: DWORD; dmICMMethod: DWORD; dmICMIntent: DWORD; dmMediaType: DWORD; dmDitherType: DWORD; dmICCManufacturer: DWORD; dmICCModel: DWORD; dmPanningWidth: DWORD; dmPanningHeight: DWORD; end;
and this is the full declaration including the dmDisplayOrientation field
typedef struct _devicemode { TCHAR dmDeviceName[CCHDEVICENAME]; WORD dmSpecVersion; WORD dmDriverVersion; WORD dmSize; WORD dmDriverExtra; DWORD dmFields; union { struct { short dmOrientation; short dmPaperSize; short dmPaperLength; short dmPaperWidth; short dmScale; short dmCopies; short dmDefaultSource; short dmPrintQuality; }; struct { POINTL dmPosition; DWORD dmDisplayOrientation; DWORD dmDisplayFixedOutput; }; }; short dmColor; short dmDuplex; short dmYResolution; short dmTTOption; short dmCollate; TCHAR dmFormName[CCHFORMNAME]; WORD dmLogPixels; DWORD dmBitsPerPel; DWORD dmPelsWidth; DWORD dmPelsHeight; union { DWORD dmDisplayFlags; DWORD dmNup; }; DWORD dmDisplayFrequency; #if (WINVER >= 0x0400) DWORD dmICMMethod; DWORD dmICMIntent; DWORD dmMediaType; DWORD dmDitherType; DWORD dmReserved1; DWORD dmReserved2; #if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400) DWORD dmPanningWidth; DWORD dmPanningHeight; #endif #endif } DEVMODE, *PDEVMODE, *LPDEVMODE;
As you can see the missing fields are dmPosition, dmDisplayOrientation and dmDisplayFixedOutput. to handle this situation we can declare a new devicemode record including these fields or another option is determine the offset of the missing fields in the _devicemode record an then use the Move procedure to Get a Set the desired value.
If we choose create a new record including the missing fields, the new devicemode record will look like this.
type _devicemode = record dmDeviceName: array [0..CCHDEVICENAME - 1] of {$IFDEF UNICODE} WideChar {$ELSE} AnsiChar {$ENDIF}; dmSpecVersion: WORD; dmDriverVersion: WORD; dmSize: WORD; dmDriverExtra: WORD; dmFields: DWORD; union1: record case Integer of 0: ( dmOrientation: Smallint; dmPaperSize: Smallint; dmPaperLength: Smallint; dmPaperWidth: Smallint; dmScale: Smallint; dmCopies: Smallint; dmDefaultSource: Smallint; dmPrintQuality: Smallint); 1: ( dmPosition: TPointL; dmDisplayOrientation: DWORD; dmDisplayFixedOutput: DWORD); end; dmColor: Shortint; dmDuplex: Shortint; dmYResolution: Shortint; dmTTOption: Shortint; dmCollate: Shortint; dmFormName: array [0..CCHFORMNAME - 1] of {$IFDEF UNICODE} WideChar {$ELSE} AnsiChar {$ENDIF}; dmLogPixels: WORD; dmBitsPerPel: DWORD; dmPelsWidth: DWORD; dmPelsHeight: DWORD; dmDiusplayFlags: DWORD; dmDisplayFrequency: DWORD; dmICMMethod: DWORD; dmICMIntent: DWORD; dmMediaType: DWORD; dmDitherType: DWORD; dmReserved1: DWORD; dmReserved2: DWORD; dmPanningWidth: DWORD; dmPanningHeight: DWORD; end; devicemode = _devicemode; Pdevicemode = ^devicemode;
Now we can write our function to change the display orientation.
Option 1) using the the new version of the devicemode record.
procedure ChangeOrientation(NewOrientation:DWORD); type _devicemode = record dmDeviceName: array [0..CCHDEVICENAME - 1] of {$IFDEF UNICODE} WideChar {$ELSE} AnsiChar {$ENDIF}; dmSpecVersion: WORD; dmDriverVersion: WORD; dmSize: WORD; dmDriverExtra: WORD; dmFields: DWORD; union1: record case Integer of // printer only fields 0: ( dmOrientation: Smallint; dmPaperSize: Smallint; dmPaperLength: Smallint; dmPaperWidth: Smallint; dmScale: Smallint; dmCopies: Smallint; dmDefaultSource: Smallint; dmPrintQuality: Smallint); // display only fields 1: ( dmPosition: TPointL; dmDisplayOrientation: DWORD; dmDisplayFixedOutput: DWORD); end; dmColor: Shortint; dmDuplex: Shortint; dmYResolution: Shortint; dmTTOption: Shortint; dmCollate: Shortint; dmFormName: array [0..CCHFORMNAME - 1] of {$IFDEF UNICODE} WideChar {$ELSE} AnsiChar {$ENDIF}; dmLogPixels: WORD; dmBitsPerPel: DWORD; dmPelsWidth: DWORD; dmPelsHeight: DWORD; dmDiusplayFlags: DWORD; dmDisplayFrequency: DWORD; dmICMMethod: DWORD; dmICMIntent: DWORD; dmMediaType: DWORD; dmDitherType: DWORD; dmReserved1: DWORD; dmReserved2: DWORD; dmPanningWidth: DWORD; dmPanningHeight: DWORD; end; devicemode = _devicemode; Pdevicemode = ^devicemode; var dm : TDeviceMode; dwTemp : DWORD; begin ZeroMemory(@dm, sizeof(dm)); dm.dmSize := sizeof(dm); //get the current settings if EnumDisplaySettings(nil, DWORD(ENUM_CURRENT_SETTINGS), dm) then begin //Now this part is very important : //when we change the orientation also we are changing resolution of the screen //example : if the current orientation is 1024x768x0 (0 is the default orientation) and we need set the orientation to 90 degrees we must swap the values of the width and height , so the new //resolution will be (768x1024x90) //the next lines makes this trick using the values of the current and new orientation if Odd(Pdevicemode(@dm)^.union1.dmDisplayOrientation)<>Odd(NewOrientation) then begin dwTemp := dm.dmPelsHeight; dm.dmPelsHeight:= dm.dmPelsWidth; dm.dmPelsWidth := dwTemp; end; //Now casting the Windows.TDeviceMode record with our devicemode record if Pdevicemode(@dm)^.union1.dmDisplayOrientation<>NewOrientation then begin //casting again to set the new orientation Pdevicemode(@dm)^.union1.dmDisplayOrientation := NewOrientation; //setting the new orientation if (ChangeDisplaySettings(dm, 0)<>DISP_CHANGE_SUCCESSFUL) then RaiseLastOSError; end; end; end;
Option 2) using the offset of the fields of the TDeviceMode record
procedure ChangeOrientation(NewOrientation:DWORD); var dm : TDeviceMode; dwTemp : DWORD; dmDisplayOrientation : DWORD; begin ZeroMemory(@dm, sizeof(dm)); dm.dmSize := sizeof(dm); if EnumDisplaySettings(nil, DWORD(ENUM_CURRENT_SETTINGS), dm) then begin //In the TDevMode record the offset of the dmScale field is equal to the position of the dmDisplayOrientation field //so using the move procedure we can get the value of the dmDisplayOrientation field Move(dm.dmScale,dmDisplayOrientation,SizeOf(dmDisplayOrientation)); //See the coments in the pprevious method // swap width and height if Odd(dmDisplayOrientation)<>Odd(NewOrientation) then begin dwTemp := dm.dmPelsHeight; dm.dmPelsHeight:= dm.dmPelsWidth; dm.dmPelsWidth := dwTemp; end; if dmDisplayOrientation<>NewOrientation then begin //set the value of the dmDisplayOrientation Move(NewOrientation,dm.dmScale,SizeOf(NewOrientation)); if (ChangeDisplaySettings(dm, 0)<>DISP_CHANGE_SUCCESSFUL) then RaiseLastOSError; end; end; end;
finally this is a sample console application to test the previous method
this code will only work if your device supports the respective display settings.
program AppChangeOrientation; {$APPTYPE CONSOLE} uses Windows, SysUtils; const DM_DISPLAYORIENTATION = $00800000; ENUM_CURRENT_SETTINGS =-1; DMDO_DEFAULT : DWORD = 0; DMDO_90 : DWORD = 1; DMDO_180 : DWORD = 2; DMDO_270 : DWORD = 3; procedure ChangeOrientation(NewOrientation:DWORD); var dm : TDeviceMode; dwTemp : DWORD; dmDisplayOrientation : DWORD; begin ZeroMemory(@dm, sizeof(dm)); dm.dmSize := sizeof(dm); if EnumDisplaySettings(nil, DWORD(ENUM_CURRENT_SETTINGS), dm) then begin Move(dm.dmScale,dmDisplayOrientation,SizeOf(dmDisplayOrientation)); // swap width and height if Odd(dmDisplayOrientation)<>Odd(NewOrientation) then begin dwTemp := dm.dmPelsHeight; dm.dmPelsHeight:= dm.dmPelsWidth; dm.dmPelsWidth := dwTemp; end; if dmDisplayOrientation<>NewOrientation then begin Move(NewOrientation,dm.dmScale,SizeOf(NewOrientation)); if (ChangeDisplaySettings(dm, 0)<>DISP_CHANGE_SUCCESSFUL) then RaiseLastOSError; end; end; end; begin try ChangeOrientation(DMDO_180); Writeln('Changed to 180'); Readln; ChangeOrientation(DMDO_270); Writeln('Changed to 270'); Readln; ChangeOrientation(DMDO_90); Writeln('Changed to 90'); Readln; ChangeOrientation(DMDO_DEFAULT); Writeln('Default Orientation restored'); Readln; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; readln; end.
Pingback: Changing Screen Orientation Programmatically using Delphi
February 11, 2011 at 9:53 pm
Nice work!
You make me know how to declare record according to the struct from c or c++ with union!