diff --git a/G940LEDControl/Forms/ButtonSelectFrm.dfm b/G940LEDControl/Forms/ButtonSelectFrm.dfm
new file mode 100644
index 0000000..461ceda
--- /dev/null
+++ b/G940LEDControl/Forms/ButtonSelectFrm.dfm
@@ -0,0 +1,94 @@
+object ButtonSelectForm: TButtonSelectForm
+ Left = 0
+ Top = 0
+ BorderIcons = [biSystemMenu]
+ BorderStyle = bsDialog
+ Caption = 'Select joystick button'
+ ClientHeight = 134
+ ClientWidth = 484
+ Color = clBtnFace
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ OldCreateOrder = False
+ Position = poMainFormCenter
+ OnCreate = FormCreate
+ OnDestroy = FormDestroy
+ OnShow = FormShow
+ DesignSize = (
+ 484
+ 134)
+ PixelsPerInch = 96
+ TextHeight = 13
+ object lblDevice: TLabel
+ Left = 8
+ Top = 11
+ Width = 42
+ Height = 13
+ Caption = 'Joystick:'
+ end
+ object lblStatus: TLabel
+ Left = 80
+ Top = 48
+ Width = 396
+ Height = 13
+ Anchors = [akLeft, akTop, akRight]
+ AutoSize = False
+ Caption = '[runtime: acquired status]'
+ ExplicitWidth = 296
+ end
+ object lblButton: TLabel
+ Left = 8
+ Top = 70
+ Width = 36
+ Height = 13
+ Caption = 'Button:'
+ end
+ object cmbDevice: TComboBox
+ Left = 80
+ Top = 8
+ Width = 396
+ Height = 21
+ Style = csDropDownList
+ Anchors = [akLeft, akTop, akRight]
+ DropDownCount = 20
+ TabOrder = 0
+ OnChange = cmbDeviceChange
+ end
+ object btnOK: TButton
+ Left = 320
+ Top = 101
+ Width = 75
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Caption = 'OK'
+ Default = True
+ ModalResult = 1
+ TabOrder = 1
+ ExplicitTop = 94
+ end
+ object btnCancel: TButton
+ Left = 401
+ Top = 101
+ Width = 75
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Cancel = True
+ Caption = 'Cancel'
+ ModalResult = 2
+ TabOrder = 2
+ ExplicitTop = 94
+ end
+ object edtButton: TEdit
+ Left = 80
+ Top = 67
+ Width = 396
+ Height = 21
+ Anchors = [akLeft, akTop, akRight]
+ Color = clBtnFace
+ ReadOnly = True
+ TabOrder = 3
+ end
+end
diff --git a/G940LEDControl/Forms/ButtonSelectFrm.pas b/G940LEDControl/Forms/ButtonSelectFrm.pas
new file mode 100644
index 0000000..575a45c
--- /dev/null
+++ b/G940LEDControl/Forms/ButtonSelectFrm.pas
@@ -0,0 +1,441 @@
+unit ButtonSelectFrm;
+
+interface
+uses
+ Classes,
+ Controls,
+ Forms,
+ StdCtrls,
+ SyncObjs,
+
+ DirectInput,
+ OtlComm,
+ OtlEventMonitor,
+ OtlTaskControl;
+
+type
+ TDeviceType = (dtGeneric, dtG940Joystick, dtG940Throttle);
+
+ TButtonSelectForm = class(TForm)
+ lblDevice: TLabel;
+ cmbDevice: TComboBox;
+ btnOK: TButton;
+ btnCancel: TButton;
+ lblStatus: TLabel;
+ lblButton: TLabel;
+ edtButton: TEdit;
+
+ procedure FormCreate(Sender: TObject);
+ procedure FormDestroy(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ procedure cmbDeviceChange(Sender: TObject);
+ private
+ FDirectInput: IDirectInput8;
+ FDeviceIndex: Integer;
+ FDevice: IDirectInputDevice8;
+ FDeviceType: TDeviceType;
+
+ FEventMonitor: TOmniEventMonitor;
+ FEventTask: IOmniTaskControl;
+ FDeviceEvent: TEvent;
+
+ FButtons: array[0..31] of Boolean;
+ FLastButtonIndex: Integer;
+ FLastButtonText: string;
+ function GetDeviceName: string;
+ protected
+ procedure AcquireDevice;
+ procedure DoAcquireDevice;
+ procedure ReleaseDevice;
+ procedure CheckDeviceState;
+ procedure ButtonPressed(AButtonIndex: Integer);
+
+ procedure SetAcquiredStatus(AAcquired: Boolean; const AMessage: string);
+
+ procedure TaskMessage(const ATask: IOmniTaskControl; const AMessage: TOmniMessage);
+ procedure TaskTerminated(const ATask: IOmniTaskControl);
+
+ property DirectInput: IDirectInput8 read FDirectInput;
+ property DeviceIndex: Integer read FDeviceIndex;
+ property Device: IDirectInputDevice8 read FDevice;
+ property DeviceType: TDeviceType read FDeviceType;
+ property DeviceName: string read GetDeviceName;
+ public
+ class function Execute(var ADeviceGUID: TGUID; var AButton: Integer; out ADisplayText: string): Boolean;
+ end;
+
+
+implementation
+uses
+ Graphics,
+ SysUtils,
+ Windows,
+
+ LogiJoystickDLL;
+
+
+type
+ TDeviceInfo = class(TObject)
+ private
+ FInstanceGUID: TGUID;
+ FProductGUID: TGUID;
+ public
+ property InstanceGUID: TGUID read FInstanceGUID write FInstanceGUID;
+ property ProductGUID: TGUID read FProductGUID write FProductGUID;
+ end;
+
+
+ TEventTask = class(TOmniWorker)
+ private
+ FEvent: THandle;
+ protected
+ function Initialize: Boolean; override;
+
+ procedure EventSignaled;
+ public
+ constructor Create(AEvent: THandle);
+ end;
+
+
+const
+ MSG_EVENT_SIGNALED = 1;
+
+
+{$R *.dfm}
+
+
+function EnumDevicesProc(var lpddi: TDIDeviceInstanceW; pvRef: Pointer): BOOL; stdcall;
+var
+ items: TStrings;
+ info: TDeviceInfo;
+
+begin
+ items := TStrings(pvRef);
+
+ info := TDeviceInfo.Create;
+ info.InstanceGUID := lpddi.guidInstance;
+ info.ProductGUID := lpddi.guidProduct;
+
+ items.AddObject(String(lpddi.tszProductName), info);
+ Result := True;
+end;
+
+
+{ TButtonSelectForm }
+class function TButtonSelectForm.Execute(var ADeviceGUID: TGUID; var AButton: Integer;
+ out ADisplayText: string): Boolean;
+begin
+ with Self.Create(Application) do
+ try
+ Result := (ShowModal = mrOk);
+
+ if Result then
+ begin
+ AButton := FLastButtonIndex;
+ ADisplayText := FLastButtonText;
+ end;
+ finally
+ Free;
+ end;
+end;
+
+
+procedure TButtonSelectForm.FormCreate(Sender: TObject);
+begin
+ FEventMonitor := TOmniEventMonitor.Create(Self);
+ FEventMonitor.OnTaskMessage := TaskMessage;
+ FEventMonitor.OnTaskTerminated := TaskTerminated;
+
+ FDeviceEvent := TEvent.Create(nil, False, False, '');
+
+ lblStatus.Caption := '';
+
+ FDeviceIndex := -1;
+end;
+
+
+procedure TButtonSelectForm.FormDestroy(Sender: TObject);
+var
+ itemIndex: Integer;
+
+begin
+ ReleaseDevice;
+
+ for itemIndex := Pred(cmbDevice.Items.Count) downto 0 do
+ TDeviceInfo(cmbDevice.Items.Objects[itemIndex]).Free;
+
+ FreeAndNil(FDeviceEvent);
+end;
+
+
+procedure TButtonSelectForm.FormShow(Sender: TObject);
+begin
+ Screen.Cursor := crHourGlass;
+ try
+ Self.Show;
+ Self.Update;
+
+ if DirectInput8Create(SysInit.HInstance, DIRECTINPUT_VERSION, IDirectInput8, FDirectInput, nil) <> S_OK then
+ raise Exception.Create('Failed to initialize DirectInput');
+
+ cmbDevice.Items.BeginUpdate;
+ try
+ cmbDevice.Items.Clear;
+ DirectInput.EnumDevices(DI8DEVCLASS_GAMECTRL,
+ EnumDevicesProc,
+ Pointer(cmbDevice.Items),
+ DIEDFL_ATTACHEDONLY);
+
+ // todo set ItemIndex to previous productGUID
+
+ cmbDevice.ItemIndex := 0;
+ finally
+ cmbDevice.Items.EndUpdate;
+ end;
+
+ if cmbDevice.ItemIndex > -1 then
+ AcquireDevice;
+ finally
+ Screen.Cursor := crDefault;
+ end;
+end;
+
+
+procedure TButtonSelectForm.cmbDeviceChange(Sender: TObject);
+begin
+ AcquireDevice;
+end;
+
+
+procedure TButtonSelectForm.AcquireDevice;
+var
+ info: TDeviceInfo;
+ vendorID: Word;
+ productID: Word;
+
+begin
+ if cmbDevice.ItemIndex <> DeviceIndex then
+ begin
+ ReleaseDevice;
+
+ if cmbDevice.ItemIndex > -1 then
+ begin
+ info := TDeviceInfo(cmbDevice.Items.Objects[cmbDevice.ItemIndex]);
+
+ FDeviceType := dtGeneric;
+ vendorID := LOWORD(info.ProductGUID.D1);
+ productID := HIWORD(info.ProductGUID.D1);
+
+ if vendorID = VENDOR_LOGITECH then
+ begin
+ case productID of
+ PRODUCT_G940_JOYSTICK: FDeviceType := dtG940Joystick;
+ PRODUCT_G940_THROTTLE: FDeviceType := dtG940Throttle;
+ end;
+ end;
+
+
+ if DirectInput.CreateDevice(info.InstanceGUID, FDevice, nil) = S_OK then
+ begin
+ Device.SetCooperativeLevel(0, DISCL_NONEXCLUSIVE or DISCL_BACKGROUND);
+ Device.SetDataFormat(c_dfDIJoystick);
+
+ DoAcquireDevice;
+ end else
+ begin
+ ReleaseDevice;
+ SetAcquiredStatus(False, 'Could not connect to device (CreateDevice failed)');
+ end;
+ end;
+ end;
+end;
+
+
+procedure TButtonSelectForm.DoAcquireDevice;
+begin
+ FDeviceEvent.ResetEvent;
+ Device.SetEventNotification(FDeviceEvent.Handle);
+
+ if Device.Acquire = DI_OK then
+ begin
+ FDeviceIndex := cmbDevice.ItemIndex;
+ SetAcquiredStatus(True, 'Press a button on the joystick to select it');
+
+ FEventTask := FEventMonitor.Monitor(CreateTask(TEventTask.Create(FDeviceEvent.Handle))).Run;
+ end else
+ begin
+ ReleaseDevice;
+ SetAcquiredStatus(False, 'Could not connect to device (acquire failed)');
+ end;
+end;
+
+
+procedure TButtonSelectForm.ReleaseDevice;
+begin
+ if Assigned(Device) then
+ begin
+ Device.SetEventNotification(0);
+ FDevice := nil;
+ end;
+
+ FDeviceIndex := -1;
+ FDeviceType := dtGeneric;
+
+ if Assigned(FEventTask) then
+ begin
+ FEventTask.Terminate;
+ FEventTask := nil;
+ end;
+
+ FillChar(FButtons, SizeOf(FButtons), 0);
+
+ edtButton.Text := '';
+
+ FLastButtonIndex := -1;
+ FLastButtonText := '';
+end;
+
+
+procedure TButtonSelectForm.CheckDeviceState;
+var
+ state: DIJOYSTATE;
+ status: Integer;
+ buttonIndex: Integer;
+ down: Boolean;
+
+begin
+ FillChar(state, SizeOf(state), 0);
+ status := Device.GetDeviceState(SizeOf(state), @state);
+
+ case status of
+ DI_OK:
+ begin
+ for buttonIndex := Low(state.rgbButtons) to High(state.rgbButtons) do
+ begin
+ down := ((state.rgbButtons[buttonIndex] and $80) <> 0);
+
+ if down and (not FButtons[buttonIndex]) then
+ ButtonPressed(buttonIndex);
+
+ FButtons[buttonIndex] := down;
+ end;
+ end;
+
+ DIERR_INPUTLOST,
+ DIERR_NOTACQUIRED:
+ DoAcquireDevice;
+ end;
+end;
+
+
+procedure TButtonSelectForm.ButtonPressed(AButtonIndex: Integer);
+const
+ G940_JOYSTICK_BUTTONS: array[0..8] of string =
+ (
+ 'Trigger',
+ 'Fire',
+ 'S1',
+ 'S2',
+ 'S3',
+ 'S4',
+ 'S5',
+ 'Mini Button',
+ 'Trigger Button'
+ );
+
+ G940_THROTTLE_BUTTONS: array[0..11] of string =
+ (
+ 'T1',
+ 'T2',
+ 'T3',
+ 'T4',
+ 'P1',
+ 'P2',
+ 'P3',
+ 'P4',
+ 'P5',
+ 'P6',
+ 'P7',
+ 'P8'
+ );
+
+var
+ buttonText: string;
+
+begin
+ buttonText := Format('Button #%d', [Succ(AButtonIndex)]);
+
+ case DeviceType of
+ dtG940Joystick:
+ if AButtonIndex in [Low(G940_JOYSTICK_BUTTONS)..High(G940_JOYSTICK_BUTTONS)] then
+ buttonText := G940_JOYSTICK_BUTTONS[AButtonIndex];
+
+ dtG940Throttle:
+ if AButtonIndex in [Low(G940_THROTTLE_BUTTONS)..High(G940_THROTTLE_BUTTONS)] then
+ buttonText := G940_THROTTLE_BUTTONS[AButtonIndex];
+ end;
+
+ edtButton.Text := buttonText;
+
+ FLastButtonIndex := AButtonIndex;
+ FLastButtonText := Format('%s on %s', [buttonText, GetDeviceName]);
+end;
+
+
+procedure TButtonSelectForm.SetAcquiredStatus(AAcquired: Boolean; const AMessage: string);
+begin
+ if AAcquired then
+ lblStatus.Font.Color := clGreen
+ else
+ lblStatus.Font.Color := clMaroon;
+
+ lblStatus.Caption := AMessage;
+end;
+
+
+procedure TButtonSelectForm.TaskMessage(const ATask: IOmniTaskControl; const AMessage: TOmniMessage);
+begin
+ if AMessage.MsgID = MSG_EVENT_SIGNALED then
+ CheckDeviceState;
+end;
+
+
+procedure TButtonSelectForm.TaskTerminated(const ATask: IOmniTaskControl);
+begin
+ if ATask = FEventTask then
+ FEventTask := nil;
+end;
+
+
+function TButtonSelectForm.GetDeviceName: string;
+begin
+ Result := '';
+
+ if cmbDevice.ItemIndex > -1 then
+ Result := cmbDevice.Items[cmbDevice.ItemIndex];
+end;
+
+
+{ TEventTask }
+constructor TEventTask.Create(AEvent: THandle);
+begin
+ inherited Create;
+
+ FEvent := AEvent;
+end;
+
+
+function TEventTask.Initialize: Boolean;
+begin
+ Result := inherited Initialize;
+
+ Task.RegisterWaitObject(FEvent, EventSignaled);
+end;
+
+
+procedure TEventTask.EventSignaled;
+begin
+ Task.Comm.Send(MSG_EVENT_SIGNALED);
+end;
+
+end.
diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm
index 1154598..6eb6928 100644
--- a/G940LEDControl/Forms/MainFrm.dfm
+++ b/G940LEDControl/Forms/MainFrm.dfm
@@ -4,7 +4,7 @@ object MainForm: TMainForm
BorderIcons = [biSystemMenu, biMinimize]
BorderStyle = bsSingle
Caption = 'G940 LED Control'
- ClientHeight = 461
+ ClientHeight = 513
ClientWidth = 465
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
@@ -23,7 +23,7 @@ object MainForm: TMainForm
Left = 8
Top = 80
Width = 449
- Height = 373
+ Height = 425
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
@@ -33,188 +33,6 @@ object MainForm: TMainForm
TabOrder = 1
object tsFSX: TTabSheet
Caption = 'Flight Simulator X'
- object gbFSXButtons: TGroupBox
- AlignWithMargins = True
- Left = 6
- Top = 75
- Width = 429
- Height = 251
- Margins.Left = 6
- Margins.Top = 6
- Margins.Right = 6
- Margins.Bottom = 6
- Align = alTop
- Caption = ' Button configuration '
- TabOrder = 1
- DesignSize = (
- 429
- 251)
- object lblFSXP1: TLabel
- Left = 12
- Top = 27
- Width = 12
- Height = 13
- Caption = 'P1'
- end
- object lblFSXP2: TLabel
- Left = 12
- Top = 54
- Width = 12
- Height = 13
- Caption = 'P2'
- end
- object lblFSXP3: TLabel
- Left = 12
- Top = 81
- Width = 12
- Height = 13
- Caption = 'P3'
- end
- object lblFSXP4: TLabel
- Left = 12
- Top = 108
- Width = 12
- Height = 13
- Caption = 'P4'
- end
- object lblFSXP5: TLabel
- Left = 12
- Top = 135
- Width = 12
- Height = 13
- Caption = 'P5'
- end
- object lblFSXP6: TLabel
- Left = 12
- Top = 162
- Width = 12
- Height = 13
- Caption = 'P6'
- end
- object lblFSXP7: TLabel
- Left = 12
- Top = 189
- Width = 12
- Height = 13
- Caption = 'P7'
- end
- object lblFSXP8: TLabel
- Left = 12
- Top = 216
- Width = 12
- Height = 13
- Caption = 'P8'
- end
- object cmbFSXP1: TComboBoxEx
- Left = 69
- Top = 24
- Width = 348
- Height = 22
- ItemsEx = <>
- Style = csExDropDownList
- Anchors = [akLeft, akTop, akRight]
- Font.Charset = DEFAULT_CHARSET
- Font.Color = clWindowText
- Font.Height = -11
- Font.Name = 'Tahoma'
- Font.Style = []
- ParentFont = False
- TabOrder = 0
- OnChange = FunctionComboBoxChange
- DropDownCount = 20
- end
- object cmbFSXP2: TComboBoxEx
- Tag = 1
- Left = 69
- Top = 50
- Width = 348
- Height = 22
- ItemsEx = <>
- Style = csExDropDownList
- Anchors = [akLeft, akTop, akRight]
- TabOrder = 1
- OnChange = FunctionComboBoxChange
- DropDownCount = 20
- end
- object cmbFSXP3: TComboBoxEx
- Tag = 2
- Left = 69
- Top = 78
- Width = 348
- Height = 22
- ItemsEx = <>
- Style = csExDropDownList
- Anchors = [akLeft, akTop, akRight]
- TabOrder = 2
- OnChange = FunctionComboBoxChange
- DropDownCount = 20
- end
- object cmbFSXP4: TComboBoxEx
- Tag = 3
- Left = 69
- Top = 105
- Width = 348
- Height = 22
- ItemsEx = <>
- Style = csExDropDownList
- Anchors = [akLeft, akTop, akRight]
- TabOrder = 3
- OnChange = FunctionComboBoxChange
- DropDownCount = 20
- end
- object cmbFSXP5: TComboBoxEx
- Tag = 4
- Left = 69
- Top = 131
- Width = 348
- Height = 22
- ItemsEx = <>
- Style = csExDropDownList
- Anchors = [akLeft, akTop, akRight]
- TabOrder = 4
- OnChange = FunctionComboBoxChange
- DropDownCount = 20
- end
- object cmbFSXP6: TComboBoxEx
- Tag = 5
- Left = 69
- Top = 159
- Width = 348
- Height = 22
- ItemsEx = <>
- Style = csExDropDownList
- Anchors = [akLeft, akTop, akRight]
- TabOrder = 5
- OnChange = FunctionComboBoxChange
- DropDownCount = 20
- end
- object cmbFSXP7: TComboBoxEx
- Tag = 6
- Left = 69
- Top = 186
- Width = 348
- Height = 22
- ItemsEx = <>
- Style = csExDropDownList
- Anchors = [akLeft, akTop, akRight]
- TabOrder = 6
- OnChange = FunctionComboBoxChange
- DropDownCount = 20
- end
- object cmbFSXP8: TComboBoxEx
- Tag = 7
- Left = 69
- Top = 213
- Width = 348
- Height = 22
- ItemsEx = <>
- Style = csExDropDownList
- Anchors = [akLeft, akTop, akRight]
- TabOrder = 7
- OnChange = FunctionComboBoxChange
- DropDownCount = 20
- end
- end
object gbFSXConnection: TGroupBox
AlignWithMargins = True
Left = 6
@@ -255,6 +73,441 @@ object MainForm: TMainForm
OnClick = btnFSXDisconnectClick
end
end
+ object pcFSXOptions: TPageControl
+ AlignWithMargins = True
+ Left = 6
+ Top = 75
+ Width = 429
+ Height = 316
+ Margins.Left = 6
+ Margins.Top = 6
+ Margins.Right = 6
+ Margins.Bottom = 6
+ ActivePage = tsFSXLEDButtons
+ Align = alClient
+ TabOrder = 1
+ object tsFSXLEDButtons: TTabSheet
+ Caption = 'LED Buttons'
+ object gbFSXButtons: TGroupBox
+ AlignWithMargins = True
+ Left = 6
+ Top = 6
+ Width = 409
+ Height = 251
+ Margins.Left = 6
+ Margins.Top = 6
+ Margins.Right = 6
+ Margins.Bottom = 6
+ Align = alTop
+ Caption = ' Button configuration '
+ TabOrder = 0
+ DesignSize = (
+ 409
+ 251)
+ object lblFSXP1: TLabel
+ Left = 12
+ Top = 27
+ Width = 12
+ Height = 13
+ Caption = 'P1'
+ end
+ object lblFSXP2: TLabel
+ Left = 12
+ Top = 54
+ Width = 12
+ Height = 13
+ Caption = 'P2'
+ end
+ object lblFSXP3: TLabel
+ Left = 12
+ Top = 81
+ Width = 12
+ Height = 13
+ Caption = 'P3'
+ end
+ object lblFSXP4: TLabel
+ Left = 12
+ Top = 108
+ Width = 12
+ Height = 13
+ Caption = 'P4'
+ end
+ object lblFSXP5: TLabel
+ Left = 12
+ Top = 135
+ Width = 12
+ Height = 13
+ Caption = 'P5'
+ end
+ object lblFSXP6: TLabel
+ Left = 12
+ Top = 162
+ Width = 12
+ Height = 13
+ Caption = 'P6'
+ end
+ object lblFSXP7: TLabel
+ Left = 12
+ Top = 189
+ Width = 12
+ Height = 13
+ Caption = 'P7'
+ end
+ object lblFSXP8: TLabel
+ Left = 12
+ Top = 216
+ Width = 12
+ Height = 13
+ Caption = 'P8'
+ end
+ object cmbFSXP1: TComboBoxEx
+ Left = 69
+ Top = 24
+ Width = 328
+ Height = 22
+ ItemsEx = <>
+ Style = csExDropDownList
+ Anchors = [akLeft, akTop, akRight]
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ ParentFont = False
+ TabOrder = 0
+ OnChange = FunctionComboBoxChange
+ DropDownCount = 20
+ end
+ object cmbFSXP2: TComboBoxEx
+ Tag = 1
+ Left = 69
+ Top = 50
+ Width = 328
+ Height = 22
+ ItemsEx = <>
+ Style = csExDropDownList
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 1
+ OnChange = FunctionComboBoxChange
+ DropDownCount = 20
+ end
+ object cmbFSXP3: TComboBoxEx
+ Tag = 2
+ Left = 69
+ Top = 78
+ Width = 328
+ Height = 22
+ ItemsEx = <>
+ Style = csExDropDownList
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 2
+ OnChange = FunctionComboBoxChange
+ DropDownCount = 20
+ end
+ object cmbFSXP4: TComboBoxEx
+ Tag = 3
+ Left = 69
+ Top = 105
+ Width = 328
+ Height = 22
+ ItemsEx = <>
+ Style = csExDropDownList
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 3
+ OnChange = FunctionComboBoxChange
+ DropDownCount = 20
+ end
+ object cmbFSXP5: TComboBoxEx
+ Tag = 4
+ Left = 69
+ Top = 131
+ Width = 328
+ Height = 22
+ ItemsEx = <>
+ Style = csExDropDownList
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 4
+ OnChange = FunctionComboBoxChange
+ DropDownCount = 20
+ end
+ object cmbFSXP6: TComboBoxEx
+ Tag = 5
+ Left = 69
+ Top = 159
+ Width = 328
+ Height = 22
+ ItemsEx = <>
+ Style = csExDropDownList
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 5
+ OnChange = FunctionComboBoxChange
+ DropDownCount = 20
+ end
+ object cmbFSXP7: TComboBoxEx
+ Tag = 6
+ Left = 69
+ Top = 186
+ Width = 328
+ Height = 22
+ ItemsEx = <>
+ Style = csExDropDownList
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 6
+ OnChange = FunctionComboBoxChange
+ DropDownCount = 20
+ end
+ object cmbFSXP8: TComboBoxEx
+ Tag = 7
+ Left = 69
+ Top = 213
+ Width = 328
+ Height = 22
+ ItemsEx = <>
+ Style = csExDropDownList
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 7
+ OnChange = FunctionComboBoxChange
+ DropDownCount = 20
+ end
+ end
+ end
+ object tsFSXExtra: TTabSheet
+ Caption = 'Extra'
+ ImageIndex = 1
+ TabVisible = False
+ object GroupBox1: TGroupBox
+ AlignWithMargins = True
+ Left = 6
+ Top = 6
+ Width = 409
+ Height = 171
+ Margins.Left = 6
+ Margins.Top = 6
+ Margins.Right = 6
+ Margins.Bottom = 0
+ Align = alTop
+ Caption = ' Zoom '
+ TabOrder = 0
+ object lblFSXToggleZoomButton: TLabel
+ Left = 57
+ Top = 56
+ Width = 77
+ Height = 13
+ Caption = 'Joystick button:'
+ end
+ object lblFSXZoomDepressed: TLabel
+ Left = 59
+ Top = 111
+ Width = 151
+ Height = 13
+ Caption = 'Zoom level (button depressed):'
+ end
+ object lblFSXZoomPressed: TLabel
+ Left = 59
+ Top = 142
+ Width = 139
+ Height = 13
+ Caption = 'Zoom level (button pressed):'
+ end
+ object lblFSXToggleZoomButtonName: TLabel
+ Left = 57
+ Top = 75
+ Width = 305
+ Height = 13
+ AutoSize = False
+ Caption = '[runtime]'
+ EllipsisPosition = epEndEllipsis
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object cbFSXToggleZoom: TCheckBox
+ Left = 16
+ Top = 24
+ Width = 161
+ Height = 17
+ Caption = ' Toggle zoom level'
+ Checked = True
+ State = cbChecked
+ TabOrder = 0
+ end
+ object btnFSXToggleZoom: TButton
+ Left = 368
+ Top = 70
+ Width = 34
+ Height = 25
+ Caption = '...'
+ TabOrder = 1
+ OnClick = btnFSXToggleZoomClick
+ end
+ object cmbFSXZoomDepressed: TComboBox
+ Left = 288
+ Top = 106
+ Width = 114
+ Height = 21
+ Style = csDropDownList
+ DropDownCount = 20
+ ItemIndex = 5
+ TabOrder = 2
+ Text = '80%'
+ Items.Strings = (
+ '30%'
+ '40%'
+ '50%'
+ '60%'
+ '70%'
+ '80%'
+ '90%'
+ '100%'
+ '110%'
+ '120%'
+ '130%'
+ '140%'
+ '150%'
+ '175%'
+ '200%'
+ '250%'
+ '300%'
+ '400%')
+ end
+ object cmbFSXZoomPressed: TComboBox
+ Left = 288
+ Top = 133
+ Width = 114
+ Height = 21
+ Style = csDropDownList
+ DropDownCount = 20
+ ItemIndex = 16
+ TabOrder = 3
+ Text = '300%'
+ Items.Strings = (
+ '30%'
+ '40%'
+ '50%'
+ '60%'
+ '70%'
+ '80%'
+ '90%'
+ '100%'
+ '110%'
+ '120%'
+ '130%'
+ '140%'
+ '150%'
+ '175%'
+ '200%'
+ '250%'
+ '300%'
+ '400%')
+ end
+ end
+ object GroupBox2: TGroupBox
+ AlignWithMargins = True
+ Left = 6
+ Top = 183
+ Width = 409
+ Height = 98
+ Margins.Left = 6
+ Margins.Top = 6
+ Margins.Right = 6
+ Margins.Bottom = 0
+ Align = alTop
+ Caption = ' Engine thrust match '
+ TabOrder = 1
+ object TLabel
+ Left = 104
+ Top = 40
+ Width = 201
+ Height = 13
+ Caption = 'Sorry, configuration not implemented yet!'
+ end
+ object TLabel
+ Left = 120
+ Top = 59
+ Width = 160
+ Height = 13
+ Caption = 'Engine 1 links to 4, engine 2 to 3.'
+ end
+ end
+ end
+ end
+ end
+ object tsAbout: TTabSheet
+ Caption = 'About'
+ ImageIndex = 1
+ object lblVersionCaption: TLabel
+ Left = 16
+ Top = 67
+ Width = 39
+ Height = 13
+ Caption = 'Version:'
+ end
+ object lblVersion: TLabel
+ Left = 75
+ Top = 67
+ Width = 45
+ Height = 13
+ Caption = 'lblVersion'
+ end
+ object Label1: TLabel
+ Left = 16
+ Top = 16
+ Width = 96
+ Height = 13
+ Caption = 'G940 LED Control'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label2: TLabel
+ Left = 16
+ Top = 35
+ Width = 95
+ Height = 13
+ Caption = #169' 2011 X'#178'Software'
+ end
+ object lblWebsite: TLabel
+ Left = 16
+ Top = 112
+ Width = 43
+ Height = 13
+ Caption = 'Website:'
+ end
+ object lblEmail: TLabel
+ Left = 16
+ Top = 135
+ Width = 32
+ Height = 13
+ Caption = 'E-mail:'
+ end
+ object lblWebsiteLink: TLinkLabel
+ Left = 75
+ Top = 111
+ Width = 143
+ Height = 17
+ Caption =
+ 'http://g940.x2software.net' +
+ '/'
+ TabOrder = 0
+ OnLinkClick = lblLinkLinkClick
+ end
+ object lblEmailLink: TLinkLabel
+ Left = 75
+ Top = 134
+ Width = 126
+ Height = 17
+ Caption =
+ 'support@x2software.net' +
+ 'a>'
+ TabOrder = 1
+ OnLinkClick = lblLinkLinkClick
+ end
end
end
object pnlG940: TPanel
diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas
index ac5e463..f125931 100644
--- a/G940LEDControl/Forms/MainFrm.pas
+++ b/G940LEDControl/Forms/MainFrm.pas
@@ -57,28 +57,58 @@ type
btnFSXConnect: TButton;
btnFSXDisconnect: TButton;
lblFSXLocal: TLabel;
+ pcFSXOptions: TPageControl;
+ tsFSXLEDButtons: TTabSheet;
+ tsFSXExtra: TTabSheet;
+ GroupBox1: TGroupBox;
+ cbFSXToggleZoom: TCheckBox;
+ lblFSXToggleZoomButton: TLabel;
+ lblFSXZoomDepressed: TLabel;
+ lblFSXZoomPressed: TLabel;
+ lblFSXToggleZoomButtonName: TLabel;
+ btnFSXToggleZoom: TButton;
+ cmbFSXZoomDepressed: TComboBox;
+ cmbFSXZoomPressed: TComboBox;
+ GroupBox2: TGroupBox;
+ tsAbout: TTabSheet;
+ lblVersionCaption: TLabel;
+ lblVersion: TLabel;
+ Label1: TLabel;
+ Label2: TLabel;
+ lblWebsiteLink: TLinkLabel;
+ lblEmailLink: TLinkLabel;
+ lblWebsite: TLabel;
+ lblEmail: TLabel;
procedure FormCreate(Sender: TObject);
procedure btnRetryClick(Sender: TObject);
procedure btnFSXConnectClick(Sender: TObject);
procedure btnFSXDisconnectClick(Sender: TObject);
+ procedure btnFSXToggleZoomClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FunctionComboBoxChange(Sender: TObject);
+ procedure lblLinkLinkClick(Sender: TObject; const Link: string;
+ LinkType: TSysLinkType);
private
FEventMonitor: TOmniEventMonitor;
FStateConsumerTask: IOmniTaskControl;
FFSXComboBoxes: TComboBoxArray;
+ FFSXToggleZoomDeviceGUID: TGUID;
+ FFSXToggleZoomButtonIndex: Integer;
protected
procedure LoadFunctions(AProviderClass: TLEDStateProviderClass; AComboBoxes: TComboBoxArray);
procedure SetFunctions(AComboBoxes: TComboBoxArray);
procedure ReadFunctions(AReader: IX2PersistReader; AComboBoxes: TComboBoxArray);
+ procedure ReadFSXExtra(AReader: IX2PersistReader);
procedure WriteFunctions(AWriter: IX2PersistWriter; AComboBoxes: TComboBoxArray);
+ procedure WriteFSXExtra(AWriter: IX2PersistWriter);
procedure LoadDefaultProfile;
procedure SaveDefaultProfile;
procedure SetDeviceState(const AMessage: string; AFound: Boolean);
+ procedure SetFSXToggleZoomButton(const ADeviceGUID: TGUID; AButtonIndex: Integer; const ADisplayText: string);
procedure InitializeStateProvider(AProviderClass: TLEDStateProviderClass);
procedure FinalizeStateProvider;
@@ -102,12 +132,15 @@ implementation
uses
ComObj,
Dialogs,
+ ShellAPI,
SysUtils,
OtlCommon,
OtlTask,
+ X2UtApp,
X2UtPersistRegistry,
+ ButtonSelectFrm,
FSXLEDStateProvider,
G940LEDStateConsumer;
@@ -147,8 +180,14 @@ type
procedure TMainForm.FormCreate(Sender: TObject);
var
consumer: IOmniWorker;
-
+
begin
+ lblVersion.Caption := App.Version.FormatVersion(False);
+
+ pcConnections.ActivePageIndex := 0;
+ pcFSXOptions.ActivePageIndex := 0;
+ lblFSXToggleZoomButtonName.Caption := '';
+
FEventMonitor := TOmniEventMonitor.Create(Self);
consumer := TG940LEDStateConsumer.Create;
@@ -193,53 +232,12 @@ begin
end;
-(*
-procedure TMainForm.tmrG940InitTimer(Sender: TObject);
-
- procedure TurnOn(ALEDPosition: Integer);
- begin
- FInitGreenState := FInitGreenState or (1 shl Pred(ALEDPosition));
- end;
-
- procedure TurnOff(ALEDPosition: Integer);
- begin
- FInitGreenState := FInitGreenState and not (1 shl Pred(ALEDPosition));
- FInitRedState := FInitRedState and not (1 shl Pred(ALEDPosition));
- end;
-
+procedure TMainForm.SetFSXToggleZoomButton(const ADeviceGUID: TGUID; AButtonIndex: Integer; const ADisplayText: string);
begin
- if not Assigned(ThrottleDevice) then
- begin
- tmrG940Init.Enabled := False;
- exit;
- end;
-
- if FInitCounter = 0 then
- GetLEDs(ThrottleDevice, FInitRedState, FInitGreenState);
-
- Inc(FInitCounter);
- if FInitCounter > 8 then
- begin
- tmrG940Init.Enabled := False;
- exit;
- end;
-
- { Clear all the LEDs in a right-to-left pattern for the top row and a
- left-to-right pattern for the bottom row. Then light only the green LEDs
- in the same pattern. }
- if FInitCounter in [1..4] then
- begin
- TurnOff(5 - FInitCounter);
- TurnOff(4 + FInitCounter);
- end else
- begin
- TurnOn(5 - (FInitCounter - 4));
- TurnOn(4 + (FInitCounter - 4));
- end;
-
- SetLEDs(ThrottleDevice, FInitRedState, FInitGreenState);
+ FFSXToggleZoomDeviceGUID := ADeviceGUID;
+ FFSXToggleZoomButtonIndex := AButtonIndex;
+ lblFSXToggleZoomButtonName.Caption := ADisplayText;
end;
-*)
procedure TMainForm.LoadFunctions(AProviderClass: TLEDStateProviderClass; AComboBoxes: TComboBoxArray);
@@ -304,6 +302,31 @@ begin
end;
+procedure TMainForm.ReadFSXExtra(AReader: IX2PersistReader);
+var
+ deviceGUID: string;
+ buttonIndex: Integer;
+ displayText: string;
+
+begin
+ if AReader.BeginSection(SECTION_FSX) then
+ try
+ if AReader.ReadString('ToggleZoomDeviceGUID', deviceGUID) and
+ AReader.ReadInteger('ToggleZoomButtonIndex', buttonIndex) and
+ AReader.ReadString('ToggleZoomDisplayText', displayText) then
+ begin
+ try
+ SetFSXToggleZoomButton(StringToGUID(deviceGUID), buttonIndex, displayText);
+ except
+ on E:EConvertError do;
+ end;
+ end;
+ finally
+ AReader.EndSection;
+ end;
+end;
+
+
procedure TMainForm.WriteFunctions(AWriter: IX2PersistWriter; AComboBoxes: TComboBoxArray);
var
comboBox: TComboBoxEx;
@@ -326,9 +349,24 @@ begin
end;
+procedure TMainForm.WriteFSXExtra(AWriter: IX2PersistWriter);
+begin
+ if AWriter.BeginSection(SECTION_FSX) then
+ try
+ AWriter.WriteString('ToggleZoomDeviceGUID', GUIDToString(FFSXToggleZoomDeviceGUID));
+ AWriter.WriteInteger('ToggleZoomButtonIndex', FFSXToggleZoomButtonIndex);
+ AWriter.WriteString('ToggleZoomDisplayText', lblFSXToggleZoomButtonName.Caption);
+ // ToDo pressed / depressed levels
+ finally
+ AWriter.EndSection;
+ end;
+end;
+
+
procedure TMainForm.LoadDefaultProfile;
var
registryReader: TX2UtPersistRegistry;
+ reader: IX2PersistReader;
begin
registryReader := TX2UtPersistRegistry.Create;
@@ -336,7 +374,10 @@ begin
registryReader.RootKey := HKEY_CURRENT_USER;
registryReader.Key := KEY_DEFAULTPROFILE;
- ReadFunctions(registryReader.CreateReader, FFSXComboBoxes);
+ reader := registryReader.CreateReader;
+
+ ReadFunctions(reader, FFSXComboBoxes);
+ ReadFSXExtra(reader);
finally
FreeAndNil(registryReader);
end;
@@ -346,6 +387,7 @@ end;
procedure TMainForm.SaveDefaultProfile;
var
registryWriter: TX2UtPersistRegistry;
+ writer: IX2PersistWriter;
begin
registryWriter := TX2UtPersistRegistry.Create;
@@ -353,7 +395,9 @@ begin
registryWriter.RootKey := HKEY_CURRENT_USER;
registryWriter.Key := KEY_DEFAULTPROFILE;
- WriteFunctions(registryWriter.CreateWriter, FFSXComboBoxes);
+ writer := registryWriter.CreateWriter;
+ WriteFunctions(writer, FFSXComboBoxes);
+ WriteFSXExtra(writer);
finally
FreeAndNil(registryWriter);
end;
@@ -470,6 +514,21 @@ begin
end;
+procedure TMainForm.btnFSXToggleZoomClick(Sender: TObject);
+var
+ deviceGUID: TGUID;
+ button: Integer;
+ displayText: string;
+
+begin
+ FillChar(deviceGUID, SizeOf(deviceGUID), 0);
+ button := -1;
+
+ if TButtonSelectForm.Execute(deviceGUID, button, displayText) then
+ SetFSXToggleZoomButton(deviceGUID, button, displayText);
+end;
+
+
procedure TMainForm.btnRetryClick(Sender: TObject);
begin
btnRetry.Visible := False;
@@ -490,6 +549,13 @@ begin
end;
end;
+
+procedure TMainForm.lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType);
+begin
+ ShellExecute(Self.Handle, 'open', PChar(Link), nil, nil, SW_SHOWNORMAL);
+end;
+
+
{ TComboBoxFunctionConsumer }
constructor TComboBoxFunctionConsumer.Create(AComboBox: TComboBoxEx);
begin
diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr
index 1338ecd..2e0394d 100644
--- a/G940LEDControl/G940LEDControl.dpr
+++ b/G940LEDControl/G940LEDControl.dpr
@@ -9,7 +9,8 @@ uses
LEDFunctionMap in 'Units\LEDFunctionMap.pas',
G940LEDStateConsumer in 'Units\G940LEDStateConsumer.pas',
LogiJoystickDLL in '..\Shared\LogiJoystickDLL.pas',
- SimConnect in '..\Shared\SimConnect.pas';
+ SimConnect in '..\Shared\SimConnect.pas',
+ ButtonSelectFrm in 'Forms\ButtonSelectFrm.pas' {ButtonSelectForm};
{$R *.res}
diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj
index a056bef..90392a4 100644
--- a/G940LEDControl/G940LEDControl.dproj
+++ b/G940LEDControl/G940LEDControl.dproj
@@ -8,7 +8,7 @@
VCL
13.4
True
- Debug
+ Release
Win32
1
Application
@@ -31,11 +31,23 @@
Base
true
+
+ true
+ Cfg_1
+ true
+ true
+
true
Base
true
+
+ true
+ Cfg_2
+ true
+ true
+
Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;System.Win;$(DCC_Namespace)
CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=
@@ -46,12 +58,14 @@
$(BDS)\bin\default_app.manifest
+ false
+ false
+ false
true
G940LEDControl_Icon.ico
Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
1033
$(BDS)\bin\default_app.manifest
- CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=
7.0
@@ -60,6 +74,10 @@
0
RELEASE;$(DCC_Define)
+
+ true
+ 1033
+
7.0
DEBUG;$(DCC_Define)
@@ -74,6 +92,14 @@
False
False
+
+ 2
+ 0
+ CompanyName=;FileDescription=;FileVersion=0.2.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=0.2;Comments=
+ true
+ F:\Components\X2Utils\Resources\VistaManAsInvoker.manifest
+ 1033
+
Delphi.Personality.12
@@ -115,9 +141,7 @@
- WPViewPDF - PDFViewClass
- madExceptVcl 2.0c - www.madshi.net
- ExpressPivotGrid 2 OLAP by Developer Express Inc.
+ ExpressPivotGrid 2 OLAP by Developer Express Inc.
@@ -142,6 +166,10 @@
+
+
+ dfm
+
Cfg_2
Base
diff --git a/G940LEDControl/Units/FSXLEDStateProvider.pas b/G940LEDControl/Units/FSXLEDStateProvider.pas
index eb215fd..c282b60 100644
--- a/G940LEDControl/Units/FSXLEDStateProvider.pas
+++ b/G940LEDControl/Units/FSXLEDStateProvider.pas
@@ -3,6 +3,7 @@ unit FSXLEDStateProvider;
interface
uses
Classes,
+ Messages,
SyncObjs,
Windows,
@@ -13,30 +14,50 @@ uses
const
+ { Note: do not change these values, the config demands it! }
FUNCTION_FSX_GEAR = FUNCTION_PROVIDER_OFFSET + 1;
FUNCTION_FSX_LANDINGLIGHTS = FUNCTION_PROVIDER_OFFSET + 2;
FUNCTION_FSX_INSTRUMENTLIGHTS = FUNCTION_PROVIDER_OFFSET + 3;
FUNCTION_FSX_PARKINGBRAKE = FUNCTION_PROVIDER_OFFSET + 4;
FUNCTION_FSX_ENGINE = FUNCTION_PROVIDER_OFFSET + 5;
+ FUNCTION_FSX_EXITDOOR = FUNCTION_PROVIDER_OFFSET + 6;
+ FUNCTION_FSX_STROBELIGHTS = FUNCTION_PROVIDER_OFFSET + 7;
+ FUNCTION_FSX_NAVLIGHTS = FUNCTION_PROVIDER_OFFSET + 8;
+ FUNCTION_FSX_BEACONLIGHTS = FUNCTION_PROVIDER_OFFSET + 9;
+ FUNCTION_FSX_FLAPS = FUNCTION_PROVIDER_OFFSET + 10;
+ FUNCTION_FSX_BATTERYMASTER = FUNCTION_PROVIDER_OFFSET + 11;
+ FUNCTION_FSX_AVIONICSMASTER = FUNCTION_PROVIDER_OFFSET + 12;
+
type
TFSXLEDStateProvider = class(TLEDStateProvider)
private
FSimConnectHandle: THandle;
FDefinitions: TList;
+// FLastDown: Boolean;
protected
function GetProcessMessagesInterval: Integer; override;
procedure UpdateMap;
procedure HandleDispatch(AData: PSimConnectRecv);
+ procedure HandleGearData(AData: Pointer);
+ procedure HandleLightsData(AData: Pointer);
+ procedure HandleParkingBrakeData(AData: Pointer);
+ procedure HandleEngineData(AData: Pointer);
+ procedure HandleExitDoorData(AData: Pointer);
+ procedure HandleFlapsData(AData: Pointer);
+ procedure HandleSwitchesData(AData: Pointer);
+
procedure AddVariable(ADefineID: Cardinal; ADatumName, AUnitsName: string;
ADatumType: SIMCONNECT_DATAType = SIMCONNECT_DATAType_FLOAT64;
AEpsilon: Single = 0; ADatumID: DWORD = SIMCONNECT_UNUSED);
procedure AddDefinition(ADefinition: Cardinal);
procedure ClearDefinitions;
+ procedure SetFSXLightState(AStates: Cardinal; AMask, AFunction: Integer);
+
property SimConnectHandle: THandle read FSimConnectHandle;
public
class procedure EnumFunctions(AConsumer: IFunctionConsumer); override;
@@ -67,6 +88,12 @@ const
DEFINITION_INSTRUMENTLIGHTS = 3;
DEFINITION_PARKINGBRAKE = 4;
DEFINITION_ENGINE = 5;
+ DEFINITION_THROTTLE = 6;
+ DEFINITION_EXITDOOR = 7;
+ DEFINITION_FLAPS = 8;
+ DEFINITION_SWITCHES = 9;
+
+ EVENT_ZOOM = 10;
FSX_VARIABLE_ISGEARRETRACTABLE = 'IS GEAR RETRACTABLE';
FSX_VARIABLE_GEARTOTALPCTEXTENDED = 'GEAR TOTAL PCT EXTENDED';
@@ -76,8 +103,14 @@ const
FSX_VARIABLE_ENGCOMBUSTION = 'GENERAL ENG COMBUSTION:%d';
FSX_VARIABLE_ENGFAILED = 'ENG FAILED:%d';
FSX_VARIABLE_ENGONFIRE = 'ENG ON FIRE:%d';
+ FSX_VARIABLE_ENGTHROTTLELEVERPOS = 'GENERAL ENG THROTTLE LEVER POSITION:%d';
FSX_VARIABLE_GEARDAMAGEBYSPEED = 'GEAR DAMAGE BY SPEED';
FSX_VARIABLE_GEARSPEEDEXCEEDED = 'GEAR SPEED EXCEEDED';
+ FSX_VARIABLE_CANOPYOPEN = 'CANOPY OPEN';
+ FSX_VARIABLE_FLAPSHANDLEPERCENT = 'FLAPS HANDLE PERCENT';
+ FSX_VARIABLE_AVIONICSMASTERSWITCH = 'AVIONICS MASTER SWITCH';
+ FSX_VARIABLE_ELECTRICALMASTERBATTERY = 'ELECTRICAL MASTER BATTERY';
+
FSX_UNIT_PERCENT = 'percent';
@@ -85,7 +118,10 @@ const
FSX_UNIT_BOOL = 'bool';
FSX_UNIT_NUMBER = 'number';
+ FSX_LIGHTON_NAV = $0001;
+ FSX_LIGHTON_BEACON = $0002;
FSX_LIGHTON_LANDING = $0004;
+ FSX_LIGHTON_STROBE = $0010;
FSX_LIGHTON_PANEL = $0020;
FSX_LIGHTON_CABIN = $0200;
@@ -97,23 +133,20 @@ const
FUNCTION_DESC_FSX_LANDINGLIGHTS = 'Landing lights';
FUNCTION_DESC_FSX_PARKINGBRAKE = 'Parking brake';
+ FUNCTION_DESC_FSX_EXITDOOR = 'Exit door';
+ FUNCTION_DESC_FSX_STROBELIGHTS = 'Strobe lights';
+ FUNCTION_DESC_FSX_NAVLIGHTS = 'Nav lights';
+ FUNCTION_DESC_FSX_BEACONLIGHTS = 'Beacon lights';
+ FUNCTION_DESC_FSX_FLAPS = 'Flaps';
+ FUNCTION_DESC_FSX_BATTERYMASTER = 'Battery master switch';
+ FUNCTION_DESC_FSX_AVIONICSMASTER = 'Avionics master switch';
type
- TGearData = packed record
- IsGearRetractable: Integer;
- TotalPctExtended: Double;
- DamageBySpeed: Integer;
- SpeedExceeded: Integer;
- end;
- PGearData = ^TGearData;
-
- TEngineData = packed record
+ TThrottleData = packed record
NumberOfEngines: Integer;
- Combustion: array[1..MAX_ENGINES] of Integer;
- Failed: array[1..MAX_ENGINES] of Integer;
- OnFire: array[1..MAX_ENGINES] of Integer;
+ ThrottleLeverPos: array[1..MAX_ENGINES] of Double;
end;
- PEngineData = ^TEngineData;
+
{ TFSXLEDStateProvider }
@@ -127,6 +160,14 @@ begin
AConsumer.AddFunction(FUNCTION_FSX_GEAR, FUNCTION_DESC_FSX_GEAR);
AConsumer.AddFunction(FUNCTION_FSX_LANDINGLIGHTS, FUNCTION_DESC_FSX_LANDINGLIGHTS);
AConsumer.AddFunction(FUNCTION_FSX_PARKINGBRAKE, FUNCTION_DESC_FSX_PARKINGBRAKE);
+
+ AConsumer.AddFunction(FUNCTION_FSX_EXITDOOR, FUNCTION_DESC_FSX_EXITDOOR);
+ AConsumer.AddFunction(FUNCTION_FSX_STROBELIGHTS, FUNCTION_DESC_FSX_STROBELIGHTS);
+ AConsumer.AddFunction(FUNCTION_FSX_NAVLIGHTS, FUNCTION_DESC_FSX_NAVLIGHTS);
+ AConsumer.AddFunction(FUNCTION_FSX_BEACONLIGHTS, FUNCTION_DESC_FSX_BEACONLIGHTS);
+ AConsumer.AddFunction(FUNCTION_FSX_FLAPS, FUNCTION_DESC_FSX_FLAPS);
+ AConsumer.AddFunction(FUNCTION_FSX_BATTERYMASTER, FUNCTION_DESC_FSX_BATTERYMASTER);
+ AConsumer.AddFunction(FUNCTION_FSX_AVIONICSMASTER, FUNCTION_DESC_FSX_AVIONICSMASTER);
end;
@@ -154,9 +195,11 @@ begin
raise EInitializeError.Create('SimConnect.dll could not be loaded');
if SimConnect_Open(FSimConnectHandle, APPNAME, 0, 0, 0, 0) <> S_OK then
- raise EInitializeError.Create('Connection to Flight Simulator could not be established');
+ raise EInitializeError.Create('Connection to Flight Simulator could not be established. Is it running?');
UpdateMap;
+
+ SimConnect_MapClientEventToSimEvent(SimConnectHandle, EVENT_ZOOM, 'VIEW_ZOOM_SET');
end;
@@ -178,12 +221,30 @@ procedure TFSXLEDStateProvider.ProcessMessages;
var
data: PSimConnectRecv;
dataSize: Cardinal;
+// down: Boolean;
+// level: Integer;
+// state: Integer;
begin
inherited;
while SimConnect_GetNextDispatch(SimConnectHandle, data, dataSize) = S_OK do
HandleDispatch(data);
+
+ {
+ state := GetKeyState(VK_CONTROL);
+ down := ((state and $8000) <> 0);
+ if down <> FLastDown then
+ begin
+ if down then
+ level := 4 * 64
+ else
+ level := 26;
+
+ SimConnect_TransmitClientEvent(SimConnectHandle, 0, EVENT_ZOOM, level, SIMCONNECT_GROUP_PRIORITY_STANDARD, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY);
+ FLastDown := down;
+ end;
+ }
end;
@@ -198,7 +259,7 @@ begin
if Consumer.FunctionMap.HasFunction(FUNCTION_FSX_GEAR) then
begin
AddVariable(DEFINITION_GEAR, FSX_VARIABLE_ISGEARRETRACTABLE, FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
- AddVariable(DEFINITION_GEAR, FSX_VARIABLE_GEARTOTALPCTEXTENDED, FSX_UNIT_PERCENT);
+ AddVariable(DEFINITION_GEAR, FSX_VARIABLE_GEARTOTALPCTEXTENDED, FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64);
AddVariable(DEFINITION_GEAR, FSX_VARIABLE_GEARDAMAGEBYSPEED, FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
AddVariable(DEFINITION_GEAR, FSX_VARIABLE_GEARSPEEDEXCEEDED, FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
AddDefinition(DEFINITION_GEAR);
@@ -206,7 +267,10 @@ begin
{ Lights }
if Consumer.FunctionMap.HasFunction(FUNCTION_FSX_LANDINGLIGHTS) or
- Consumer.FunctionMap.HasFunction(FUNCTION_FSX_INSTRUMENTLIGHTS) then
+ Consumer.FunctionMap.HasFunction(FUNCTION_FSX_INSTRUMENTLIGHTS) or
+ Consumer.FunctionMap.HasFunction(FUNCTION_FSX_STROBELIGHTS) or
+ Consumer.FunctionMap.HasFunction(FUNCTION_FSX_NAVLIGHTS) or
+ Consumer.FunctionMap.HasFunction(FUNCTION_FSX_BEACONLIGHTS) then
begin
AddVariable(DEFINITION_LIGHTS, FSX_VARIABLE_LIGHTONSTATES, FSX_UNIT_MASK, SIMCONNECT_DATATYPE_INT32);
AddDefinition(DEFINITION_LIGHTS);
@@ -235,93 +299,91 @@ begin
AddDefinition(DEFINITION_ENGINE);
end;
+
+ { Exit door }
+ if Consumer.FunctionMap.HasFunction(FUNCTION_FSX_EXITDOOR) then
+ begin
+ AddVariable(DEFINITION_EXITDOOR, FSX_VARIABLE_CANOPYOPEN, FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64);
+ AddDefinition(DEFINITION_EXITDOOR);
+ end;
+
+ { Flaps }
+ if Consumer.FunctionMap.HasFunction(FUNCTION_FSX_FLAPS) then
+ begin
+ AddVariable(DEFINITION_FLAPS, FSX_VARIABLE_FLAPSHANDLEPERCENT, FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64);
+ AddDefinition(DEFINITION_FLAPS);
+ end;
+
+ { Master switches }
+ if Consumer.FunctionMap.HasFunction(FUNCTION_FSX_BATTERYMASTER) or
+ Consumer.FunctionMap.HasFunction(FUNCTION_FSX_AVIONICSMASTER) then
+ begin
+ AddVariable(DEFINITION_SWITCHES, FSX_VARIABLE_AVIONICSMASTERSWITCH, FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
+ AddVariable(DEFINITION_SWITCHES, FSX_VARIABLE_ELECTRICALMASTERBATTERY, FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32);
+ AddDefinition(DEFINITION_SWITCHES);
+ end;
+
+ { Throttle }
+ {
+ AddVariable(DEFINITION_THROTTLE, FSX_VARIABLE_NUMBEROFENGINES, FSX_UNIT_NUMBER, SIMCONNECT_DATAType_INT32);
+
+ for engineIndex := 1 to MAX_ENGINES do
+ AddVariable(DEFINITION_THROTTLE, Format(FSX_VARIABLE_ENGTHROTTLELEVERPOS, [engineIndex]), FSX_UNIT_PERCENT);
+
+ AddDefinition(DEFINITION_THROTTLE);
+ }
+end;
+
+
+procedure TFSXLEDStateProvider.SetFSXLightState(AStates: Cardinal; AMask: Integer; AFunction: Integer);
+begin
+ if (AStates and AMask) <> 0 then
+ Consumer.SetStateByFunction(AFunction, lsGreen)
+ else
+ Consumer.SetStateByFunction(AFunction, lsRed);
end;
procedure TFSXLEDStateProvider.HandleDispatch(AData: PSimConnectRecv);
var
simObjectData: PSimConnectRecvSimObjectData;
- states: Cardinal;
- gearData: PGearData;
- engineData: PEngineData;
- engineIndex: Integer;
- state: TLEDState;
+ data: Pointer;
+// throttleData: PThrottleData;
begin
case SIMCONNECT_RECV_ID(AData^.dwID) of
SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
begin
simObjectData := PSimConnectRecvSimObjectData(AData);
+ data := @simObjectData^.dwData;
case simObjectData^.dwRequestID of
- DEFINITION_GEAR:
+ DEFINITION_GEAR: HandleGearData(data);
+ DEFINITION_LIGHTS: HandleLightsData(data);
+ DEFINITION_PARKINGBRAKE: HandleParkingBrakeData(data);
+ DEFINITION_ENGINE: HandleEngineData(data);
+ DEFINITION_EXITDOOR: HandleExitDoorData(data);
+ DEFINITION_FLAPS: HandleFlapsData(data);
+ DEFINITION_SWITCHES: HandleSwitchesData(data);
+ {
+ DEFINITION_THROTTLE:
begin
- gearData := @simObjectData^.dwData;
+ throttleData := @simObjectData^.dwData;
- if gearData^.DamageBySpeed <> 0 then
- Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsError)
-
- else if gearData^.SpeedExceeded <> 0 then
- Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsWarning)
-
- else if gearData^.IsGearRetractable <> 0 then
+ if throttleData^.NumberOfEngines > 2 then
begin
- case Trunc(gearData^.TotalPctExtended * 100) of
- 0: Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsRed);
- 100: Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsGreen);
- else Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsAmber);
- end;
- end else
- Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsOff);
- end;
-
- DEFINITION_LIGHTS:
- begin
- states := simObjectData^.dwData;
- if (states and FSX_LIGHTON_LANDING) <> 0 then
- Consumer.SetStateByFunction(FUNCTION_FSX_LANDINGLIGHTS, lsGreen)
- else
- Consumer.SetStateByFunction(FUNCTION_FSX_LANDINGLIGHTS, lsRed);
-
- if (states and FSX_LIGHTON_PANEL) <> 0 then
- Consumer.SetStateByFunction(FUNCTION_FSX_INSTRUMENTLIGHTS, lsGreen)
- else
- Consumer.SetStateByFunction(FUNCTION_FSX_INSTRUMENTLIGHTS, lsRed);
- end;
-
- DEFINITION_PARKINGBRAKE:
- if simObjectData^.dwData <> 0 then
- Consumer.SetStateByFunction(FUNCTION_FSX_PARKINGBRAKE, lsRed)
- else
- Consumer.SetStateByFunction(FUNCTION_FSX_PARKINGBRAKE, lsGreen);
-
- DEFINITION_ENGINE:
- begin
- engineData := @simObjectData^.dwData;
-
- if engineData^.NumberOfEngines > 0 then
- begin
- state := lsGreen;
-
- for engineIndex := 1 to Min(engineData^.NumberOfEngines, MAX_ENGINES) do
+ if (throttleData^.ThrottleLeverPos[4] <> throttleData^.ThrottleLeverPos[1]) or
+ (throttleData^.ThrottleLeverPos[3] <> throttleData^.ThrottleLeverPos[2]) then
begin
- if engineData.OnFire[engineIndex] <> 0 then
- begin
- state := lsError;
- break;
+ throttleData^.ThrottleLeverPos[4] := throttleData^.ThrottleLeverPos[1];
+ throttleData^.ThrottleLeverPos[3] := throttleData^.ThrottleLeverPos[2];
- end else if engineData.Failed[engineIndex] <> 0 then
- state := lsWarning
-
- else if (engineData.Combustion[engineIndex] = 0) and
- (state = lsGreen) then
- state := lsRed;
+ SimConnect_SetDataOnSimObject(SimConnectHandle, DEFINITION_THROTTLE, SIMCONNECT_OBJECT_ID_USER,
+ 0, 0, SizeOf(throttleData^), throttleData);
end;
-
- Consumer.SetStateByFunction(FUNCTION_FSX_ENGINE, state);
- end else
- Consumer.SetStateByFunction(FUNCTION_FSX_ENGINE, lsOff);
+ end;
end;
+ }
end;
end;
@@ -331,6 +393,175 @@ begin
end;
+procedure TFSXLEDStateProvider.HandleGearData(AData: Pointer);
+type
+ PGearData = ^TGearData;
+ TGearData = packed record
+ IsGearRetractable: Integer;
+ TotalPctExtended: Double;
+ DamageBySpeed: Integer;
+ SpeedExceeded: Integer;
+ end;
+
+var
+ gearData: PGearData;
+
+begin
+ gearData := AData;
+
+ if gearData^.DamageBySpeed <> 0 then
+ Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsError)
+
+ else if gearData^.SpeedExceeded <> 0 then
+ Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsWarning)
+
+ else if gearData^.IsGearRetractable <> 0 then
+ begin
+ case Trunc(gearData ^.TotalPctExtended * 100) of
+ 0: Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsRed);
+ 100: Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsGreen);
+ else Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsAmber);
+ end;
+ end else
+ Consumer.SetStateByFunction(FUNCTION_FSX_GEAR, lsOff);
+end;
+
+
+procedure TFSXLEDStateProvider.HandleLightsData(AData: Pointer);
+var
+ state: Cardinal;
+
+begin
+ state := PCardinal(AData)^;
+
+ SetFSXLightState(state, FSX_LIGHTON_LANDING, FUNCTION_FSX_LANDINGLIGHTS);
+ SetFSXLightState(state, FSX_LIGHTON_PANEL, FUNCTION_FSX_INSTRUMENTLIGHTS);
+ SetFSXLightState(state, FSX_LIGHTON_BEACON, FUNCTION_FSX_BEACONLIGHTS);
+ SetFSXLightState(state, FSX_LIGHTON_NAV, FUNCTION_FSX_NAVLIGHTS);
+ SetFSXLightState(state, FSX_LIGHTON_STROBE, FUNCTION_FSX_STROBELIGHTS);
+end;
+
+
+procedure TFSXLEDStateProvider.HandleParkingBrakeData(AData: Pointer);
+begin
+ if PCardinal(AData)^ <> 0 then
+ Consumer.SetStateByFunction(FUNCTION_FSX_PARKINGBRAKE, lsRed)
+ else
+ Consumer.SetStateByFunction(FUNCTION_FSX_PARKINGBRAKE, lsGreen);
+end;
+
+
+procedure TFSXLEDStateProvider.HandleEngineData(AData: Pointer);
+type
+ PEngineData = ^TEngineData;
+ TEngineData = packed record
+ NumberOfEngines: Integer;
+ Combustion: array[1..MAX_ENGINES] of Integer;
+ Failed: array[1..MAX_ENGINES] of Integer;
+ OnFire: array[1..MAX_ENGINES] of Integer;
+ end;
+
+var
+ engineData: PEngineData;
+ engineIndex: Integer;
+ state: TLEDState;
+
+begin
+ engineData := AData;
+
+ if engineData^.NumberOfEngines > 0 then
+ begin
+ state := lsGreen;
+
+ for engineIndex := 1 to Min(engineData^.NumberOfEngines, MAX_ENGINES) do
+ begin
+ if engineData^.OnFire[engineIndex] <> 0 then
+ begin
+ state := lsError;
+ break;
+
+ end else if engineData^.Failed[engineIndex] <> 0 then
+ state := lsWarning
+
+ else if (engineData^.Combustion[engineIndex] = 0) and
+ (state = lsGreen) then
+ state := lsRed;
+ end;
+
+ Consumer.SetStateByFunction(FUNCTION_FSX_ENGINE, state);
+ end else
+ Consumer.SetStateByFunction(FUNCTION_FSX_ENGINE, lsOff);
+end;
+
+
+procedure TFSXLEDStateProvider.HandleExitDoorData(AData: Pointer);
+type
+ PExitDoorData = ^TExitDoorData;
+ TExitDoorData = packed record
+ PercentOpen: Double;
+ end;
+
+var
+ exitDoorData: PExitDoorData;
+
+begin
+ exitDoorData := AData;
+
+ case Trunc(exitDoorData^.PercentOpen) of
+ 0: Consumer.SetStateByFunction(FUNCTION_FSX_EXITDOOR, lsGreen);
+ 100: Consumer.SetStateByFunction(FUNCTION_FSX_EXITDOOR, lsRed);
+ else Consumer.SetStateByFunction(FUNCTION_FSX_EXITDOOR, lsAmber);
+ end;
+end;
+
+
+procedure TFSXLEDStateProvider.HandleFlapsData(AData: Pointer);
+type
+ PFlapsData = ^TFlapsData;
+ TFlapsData = packed record
+ HandlePercent: Double;
+ end;
+
+var
+ flapsData: PFlapsData;
+
+begin
+ flapsData := AData;
+
+ case Trunc(flapsData^.HandlePercent) of
+ 0: Consumer.SetStateByFunction(FUNCTION_FSX_FLAPS, lsGreen);
+ 100: Consumer.SetStateByFunction(FUNCTION_FSX_FLAPS, lsRed);
+ else Consumer.SetStateByFunction(FUNCTION_FSX_FLAPS, lsAmber);
+ end;
+end;
+
+
+procedure TFSXLEDStateProvider.HandleSwitchesData(AData: Pointer);
+type
+ PSwitchesData = ^TSwitchesData;
+ TSwitchesData = packed record
+ AvionicsSwitch: Cardinal;
+ BatterySwitch: Cardinal;
+ end;
+
+var
+ switchesData: PSwitchesData;
+
+begin
+ switchesData := AData;
+
+ if switchesData^.AvionicsSwitch <> 0 then
+ Consumer.SetStateByFunction(FUNCTION_FSX_AVIONICSMASTER, lsGreen)
+ else
+ Consumer.SetStateByFunction(FUNCTION_FSX_AVIONICSMASTER, lsRed);
+
+ if switchesData^.BatterySwitch <> 0 then
+ Consumer.SetStateByFunction(FUNCTION_FSX_BATTERYMASTER, lsGreen)
+ else
+ Consumer.SetStateByFunction(FUNCTION_FSX_BATTERYMASTER, lsRed);
+end;
+
+
procedure TFSXLEDStateProvider.AddDefinition(ADefinition: Cardinal);
begin
FDefinitions.Add(Pointer(ADefinition));
diff --git a/G940LEDControl/Units/G940LEDStateConsumer.pas b/G940LEDControl/Units/G940LEDStateConsumer.pas
index fde7ca4..8739416 100644
--- a/G940LEDControl/Units/G940LEDStateConsumer.pas
+++ b/G940LEDControl/Units/G940LEDStateConsumer.pas
@@ -84,13 +84,14 @@ type
FRed: Byte;
FGreen: Byte;
protected
+ { IRunInMainThread }
procedure Execute;
public
constructor Create(ADevice: IDirectInputDevice8; ARed, AGreen: Byte);
end;
-function EnumDevicesProc(var lpddi: TDIDeviceInstanceA; pvRef: Pointer): BOOL; stdcall;
+function EnumDevicesProc(var lpddi: TDIDeviceInstanceW; pvRef: Pointer): BOOL; stdcall;
var
vendorID: Word;
productID: Word;
diff --git a/G940LEDControl/Units/LEDFunctionMap.pas b/G940LEDControl/Units/LEDFunctionMap.pas
index 1aff4b7..3d9dd83 100644
--- a/G940LEDControl/Units/LEDFunctionMap.pas
+++ b/G940LEDControl/Units/LEDFunctionMap.pas
@@ -57,6 +57,8 @@ const
FUNCTION_AMBER = 3;
FUNCTION_GREEN = 4;
+ { Note: if this offset ever changes, make sure to write a conversion for
+ existing configurations. And probably reserve a bit more. }
FUNCTION_PROVIDER_OFFSET = FUNCTION_GREEN;