From b01ca9b9e7447f79550f864cbdc88492d79e878d Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Mon, 13 Aug 2012 11:58:54 +0000 Subject: [PATCH] - Forgot to commit the release version - Added nav/strobe/beacon lights, flaps status, exit door status and master switch states --- G940LEDControl/Forms/ButtonSelectFrm.dfm | 94 +++ G940LEDControl/Forms/ButtonSelectFrm.pas | 441 +++++++++++++ G940LEDControl/Forms/MainFrm.dfm | 621 ++++++++++++------ G940LEDControl/Forms/MainFrm.pas | 162 +++-- G940LEDControl/G940LEDControl.dpr | 3 +- G940LEDControl/G940LEDControl.dproj | 38 +- G940LEDControl/Units/FSXLEDStateProvider.pas | 395 ++++++++--- G940LEDControl/Units/G940LEDStateConsumer.pas | 3 +- G940LEDControl/Units/LEDFunctionMap.pas | 2 + 9 files changed, 1438 insertions(+), 321 deletions(-) create mode 100644 G940LEDControl/Forms/ButtonSelectFrm.dfm create mode 100644 G940LEDControl/Forms/ButtonSelectFrm.pas 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' + 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 @@ G940LEDControl.dpr - 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 @@ + +
ButtonSelectForm
+ 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;