XPLMDisplay

To use:

import xp

This API provides the basic hooks to draw in X-Plane and create user interface. All X-Plane drawing is done in OpenGL. The X-Plane plug-in manager takes care of properly setting up the OpenGL context and matrices. You do not decide when in your code’s execution to draw; X-Plane tells you when it is ready to have your plugin draw.

X-Plane’s drawing strategy is straightforward: every “frame” the screen is rendered by drawing the 3-d scene (dome, ground, objects, airplanes, etc.) and then drawing the cockpit on top of it. Alpha blending is used to overlay the cockpit over the world (and the gauges over the panel, etc.).

There are three ways you can draw: directly, directly onto avionics screens, and in a window.

  • Direct drawing involves drawing to the screen before or after X-Plane finishes a phase of drawing. When you draw directly, you can specify whether x-plane is to complete this phase or not. This allows you to do three things: draw before x-plane does (under it), draw after x-plane does (over it), or draw instead of x-plane.

    To draw directly, you register a callback with registerDrawCallback() and specify what phase you want to intercept. The plug-in manager will call you over and over to draw that phase.

    Direct drawing allows you to override scenery, panels, or anything. Note that you cannot assume that you are the only plug-in drawing at this phase.

    See Direct Drawing.

  • Avionics drawing (X-Plane 12+) allows you to draw directly onto avionics device screens.

    Similar to Direct Drawing, you register a callback with registerAvionicsCallbacksEx() and specify which device to draw on, and whether to draw before or after X-Plane draws.

    See Avionics Drawing.

  • Window drawing provides slightly higher level functionality. With window drawing you create a window with createWindowEx() that takes up a portion of the screen. Window drawing is always two dimensional. Window drawing is front-to-back controlled; you can specify that you want your window to be brought on top, and other plug-ins may put their window on top of you. Window drawing also allows you to sign up for key presses and receive mouse clicks.

    Note: all 2-d (and thus all window drawing) is done in ‘cockpit pixels’. Even when the OpenGL window contains more than 1024x768 pixels, the cockpit drawing is magnified so that only 1024x768 pixels are available.

    See Window Drawing.

There are three ways to get keystrokes:

  • Keyboard Focus: If you create a window, the window can take keyboard focus with takeKeyboardFocus(). It will then receive all keystrokes. If no window has focus, X-Plane receives keystrokes. Use this to implement typing in dialog boxes, etc. Only one window may have focus at a time; your window will be notified if it loses focus.

  • Hot Key: If you need to associate key strokes with commands/functions in your plug-in, register a hot key with registerHotKey(). A hot key is a key-specific callback. Hotkeys are sent based on virtual key strokes, so any key may be distinctly mapped with any modifiers. Hot keys can be remapped by other plug-ins. As a plug-in, you don’t have to worry about what your hot key ends up mapped to; other plug-ins may provide a UI for remapping keystrokes. So hotkeys allow a user to resolve conflicts and customize keystrokes.

    See Hot Keys

  • Key Sniffer If you need low level access to the keystroke stream, install a key sniffer with registerKeySniffer(). Key sniffers can be installed above everything or right in front of the sim.

    See Key Sniffing

Direct Drawing

Basic drawing callbacks are for low level intercepting of render loop. The purpose of drawing callbacks is to provide targeted additions or replacements to x-plane’s graphics environment (for example, to add extra custom objects, or replace drawing of the AI aircraft). Do not assume that the drawing callbacks will be called in the order implied by the enumerations. Also do not assume that each drawing phase ends before another begins; they may be nested.

Note

Laminar says, “Note that all APIs in this section are deprecated, and will likely be removed during the X-Plane 11 run as part of the transition to Vulkan/Metal/etc. See XPLMInstance API for future-proof drawing of 3-D objects.

Register and Unregister your drawing callback(s). You may register a callback multiple times for the same or different phases as long as the reference constant is unique for each registration.

registerDrawCallback(draw, phase=Phase_Window, after=1, refCon=None)

Register a low level drawing callback.

phase indicates where in the drawing phase you wish to be called, with after indicating if you want to be called before (0), or after(1) the indicated phase. The refCon will be passed to your draw callback function. (See below for valid phases.)

On success, 1 is returned, 0 otherwise.

You may register a callback multiple times for the same or different phases as long as the refCon is unique for each time.

Your draw callback function takes three parameters (phase, after, refCon). If after is zero, you callback can return 0 to suppress further X-Plane drawing in the phase, or 1 to allow X-Plane to finish drawing. (Return value is ignored when after is 1.)

>>> def MyDraw(phase, after, refCon):
...    xp.setGraphicsState(0, 1, 0, 0, 0, 0, 0)
...    xp.drawString([.9, 0, 0], 110, 175, "Hello there", None, xp.Font_Basic)
...
>>> xp.registerDrawCallback(MyDraw)
1
../../_images/display_drawstring.png
>>> xp.unregisterDrawCallback(MyDraw)
1

If you have installed python opengl:

>>> import OpenGL.GL as GL
>>> def MyGLDraw(phase, after, refCon):
...    xp.setGraphicsState(0, 0, 0, 0, 1, 1, 0)
...    left, top, right, bottom = 100, 200, 200, 100
...    numLines = int(min(top - bottom, right - left) / 2)
...    time = int(numLines * xp.getElapsedTime()) % numLines
...    for i in range(numLines):
...         GL.glBegin(GL.GL_LINE_LOOP)
...         left += 1
...         right -= 1
...         bottom += 1
...         top -= 1
...         x = (i + time) % numLines
...         GL.glColor3f(x / numLines, (numLines - x) / numLines, x / numLines)  # change colors, for fun
...         GL.glVertex2f(left, bottom)
...         GL.glVertex2f(left, top)
...         GL.glVertex2f(right, top)
...         GL.glVertex2f(right, bottom)
...         GL.glEnd()
...
>>> xp.registerDrawCallback(MyGLDraw)
1
../../_images/display_opengl.png
>>> xp.unregisterDrawCallback(MyGLDraw)
1

Upon entry to your callback, the OpenGL context will be correctly set up for you and OpenGL will be in ‘local’ coordinates for 3d drawing and panel coordinates for 2d drawing. The OpenGL state (texturing, etc.) will be unknown.

Official SDK XPLMRegisterDrawCallback

Drawing Phases

phase indicates which part of drawing we are in. Drawing is done from the back to the front. We get a callback before or after each item. Metaphases provide access to the beginning and end of the 3d (scene) and 2d (cockpit) drawing in a manner that is independent of new phases added via x-plane implementation.

Warning

As X-Plane’s scenery evolves, some drawing phases may cease to exist and new ones may be invented. If you need a particularly specific use of these codes, consult Austin and/or be prepared to revise your code as X-Plane evolves.

Drawing Phase Value

Meaning

Phase_Modern3D = 31

A chance to do modern 3D drawing. This is supported under OpenGL and Vulkan.

It is not supported under Metal. It comes with potentially a substantial performance overhead. Please do not opt into this phase if you don’t do any actual drawing that request the depth buffer in some way!

Early indication is that this phase will not be supported in X-Plane 12. You have been warned.

Official SDK

xplm_Phase_Modern3D

Phase_FirstCockpit = 35

First phase where you can draw in 2-d.

Official SDK

xplm_Phase_FirstCockpit

Phase_Panel = 40

The non-moving parts of the aircraft panel

Official SDK

xplm_Phase_Panel

Phase_Gauges = 45

The moving parts of the aircraft panel

Official SDK

xplm_Phase_Gauges

Phase_Window = 50

Floating windows from plugins.

Official SDK

xplm_Phase_Window

Phase_LastCockpit = 55

Last chance to draw in 2d.

Official SDK

xplm_Phase_LastCockpit

This may seem obvious, but it bears repeating: Make your draw code fast. It will be executed thousands of times and every microsecond wasted will impact the user’s frame rate (FPS). See, for example, demo code in plugin PI_TextureDraw.py. A simple port from C to Python results in horrid execution times. A benefit of writing in C/C++ is the compiler is able to optimize execution. By changing some simple python code to use python’s numpy module, we were able to speed up draw times by a factor of 36 (from 65 milliseconds per frame to 1.8 msec!).

unregisterDrawCallback(draw, phase=Phase_Window, after=1, refCon=None)

Unregister a low level drawing callback. Parameters must match those provided with registerDrawCallback().

You must unregister a callback for each time you register, if you have registered it multiple times with different refCons.

Returns 1 on success, 0 otherwise.

Official SDK XPLMUnregisterDrawCallback

Avionics Drawing

Avionics callback is convenient in that is provides the proper setup for a number of glass avionics devices. When your draw function is called, OpenGL is properly set for the device’s viewport. Additionally, you can draw before, after, or instead of the X-Plane drawing for that device.

Avionics Drawing Functions

registerAvionicsCallbacksEx(deviceId, before=None, after=None, refCon=None)

Registers function to be called before and/or after X-Plane draws on the specified device.

Returns an AvionicsID which should be passed to unregisterAvionicsCallbacks(), or None on error.

Both before and after callback functions have identical signatures. Return value for after callback is ignored. Return value for before function is either 1 to indicated X-Plane should continue to draw, or 0 to indicate X-Plane should not also draw on the specified device.

Note that your callback is called even if the device is not “powered on”.

Callback functions have three parameters:

deviceID: which should match the deviceID provided with registration
inIsBefore: boolean to indicated if this callback is being called before, or after X-Plane drawing.
refCon: reference constant provide with registration.
>>> from OpenGL import GL
>>> def MyDraw(deviceID, isBefore, refCon):
...     xp.setGraphicsState(0, 1)
...     xp.drawString([1, 0, 0], 10, 10,
...                   f"Viewport size is {GL.glGetIntegerv(GL.GL_VIEWPORT)}",
...                   None, xp.Font_Basic)
...     return 1
...
>>> avionicsID = xp.registerAvionicsCallbacksEx(xp.Device_G1000_PFD_1, after=MyDraw)
../../_images/avionics_draw_g1000.png

Official SDK XPLMRegisterAvionicsCallbacksEx

unregisterAvionicsCallbacks(avionicsID)

Unregisters specified avionics callbacks.

>>> xp.unregisterAvionicsCallbacks(avionicsID)

Official SDK XPLMUnregisterAvionicsCallbacks

Device IDs

Use any of these device IDs with registerAvionicsCallbacksEx().

Device_GNS430_1 = 0

GNS430, pilot side

Device_GNS430_2 = 1

GNS430, copilot side

Device_GNS530_1 = 2

GNS530, pilot side

Device_GNS530_2 = 3

GNS530, copilot side

Device_CDU739_1 = 4

Generic airliner CDU, pilot side

Device_CDU739_2 = 5

Generic airliner CDU, copilot side

Device_G1000_PFD_1 = 6

G1000 Primary Flight Display, pilot side

Device_G1000_MFD = 7

G1000 Multifunction Display

Device_G1000_PFD_2 = 8

G1000 Primary Flight Display, copilot side

Device_CDU815_1 = 9

Primus CDU, pilot side

Device_CDU815_2 = 10

Primus CDU, copilot side

Device_Primus_PFD_1 = 11

Primus Primary Flight Display, pilot side

Device_Primus_PFD_2 = 12

Primus Primary Flight Display, copilot side

Device_Primus_MFD_1 = 13

Primus Multifunction Display, pilot side

Device_Primus_MFD_2 = 14

Primus Multifunction Display, copilot side

Device_Primus_MFD_3 = 15

Primus Multifunction Display, central

Device_Primus_RMU_1 = 16

Primus Radio Management Unit, pilot side

Device_Primus_RMU_2 = 17

Primus Radio Management Unit, copilot side

Window Drawing

The window API provides a high-level abstraction for drawing with UI interaction.

Windows are created with createWindowEx(), and have access to X-Plane 11 windowing features, like support for new positioning modes (setWindowPositioningMode()) including being “popped out” into their own first-class window in the operating system. They can also optionally be decorated in the style of X-Plane 11 windows (like the map). (The pre X-Plane 11.50 function XPLMCreateWindow is deprecated.)

Windows operate in “boxel” units. A boxel (“box of pixels”) is a unit of virtual pixels which, depending on X-Plane’s scaling, may correspond to an arbitrary NxN “box” of real pixels on screen. Because X-Plane handles this scaling automatically, you can effectively treat the units as though you where simply drawing in pixels, and know that when X-Plane is running with 150% or 200% scaling, your drawing will be automatically scaled (and likewise all mouse coordinates, screen bounds, etc. will also be auto-scaled).

Modern windows are not constrained to the main window, they have their origin in the lower left of the entire global desktop space, and the lower left of the main X-Plane window is not guaranteed to be (0, 0). X increases as you move right, and Y increases as you move up.

Note that this requires dealing with your window’s bounds in “global desktop” positioning units, rather than the traditional panel coordinate system. In global desktop coordinates, the main X-Plane window may not have its origin at coordinate (0, 0), and your own window may have negative coordinates. Assuming you don’t implicitly assume (0, 0) as your origin, the only API change you should need is to start using getMouseLocationGlobal() rather than (deprecated) XPLMGetMouseLocation(), and getScreenBoundsGlobal() instead of (deprecated) XPLMGetScreenSize().

For more information and examples on window positioning, and determining monitor bounds, see More about Window Positioning.

Note: There is no API or callback for window close: When the user closes your window, you’ll simply not get any more draw callbacks.

In addition to the basic functions createWindowEx() and destroyWindow(), functions include:

Window Drawing Functions

createWindowEx(...)
Parameters and defaults:
left=100, top=200, right=200, bottom=100,
visible=0,
draw=None, click=None, key=None, cursor=None, wheel=None,
refCon=None
decoration=WindowDecorationRoundRectangle,
layer=WindowLayerFloatingWindows,
rightClick=None,

This routine creates a new “modern” window, at location specified by left, top, right, bottom. Defaults are provided to simplify examples, but you’ll likely need something larger.

Initial visibility is set using visible, and can be queried and changed using getWindowIsVisible() and setWindowIsVisible(). By default, window is created not-visible.

Window style is indicated by decoration, and can only be specified at creation time. By default, window is created as WindowDecorationRoundRectangle and looks like:

../../_images/small_round_rect_window.png

decoration must be one of:

Decoration Value

Meaning

WindowDecorationNone = 0

X-Plane will draw no decoration for your window, and apply no automatic click handlers. The window will not stop click from passing through its bounds. This is suitable for “windows” which request, say, the full screen bounds, then only draw in a small portion of the available area.

Official SDK xplm_WindowDecorationNone

WindowDecorationRoundRectangle = 1

The default decoration for “native” windows, like the map. Provides a solid background, as well as click handlers for resizing and dragging the window.

Official SDK xplm_WindowDecorationRoundRectangle

WindowDecorationSelfDecorated = 2

X-Plane will draw no decoration for your window, nor will it provide resize handlers for your window edges, but it will stop clicks from passing through your windows bounds.

Official SDK xplm_WindowDecorationSelfDecorated

WindowDecorationSelfDecoratedResizable = 3

Like self-decorated, but with resizing; X-Plane will draw no decoration for your window, but it will stop clicks from passing through your windows bounds, and provide automatic mouse handlers for resizing.

Official SDK xplm_WindowDecorationNone

layer describes where in the ordering of windows X-Plane should place this window. Windows in higher layers cover windows in lower layers. So, a given window might be at the top of its particular layer, but it might still be obscured by a window in a higher layer. (This happens frequently when floating windows, like X-Plane’s map, are covered by a modal alert.) Layer is specified only at creation time, and is one of:

XPLMWindowLayer

Layer Value

Meaning

WindowLayerFlightOverlay = 0

The lowest layer, used for HUD-like displays while flying.

Official SDK xplm_WindowLayerFlightOverlay

WindowLayerFloatingWindows = 1

Windows that “float” over the sim, like the X-Plane 11 map does. If you are not sure which layer to create your window in, choose floating

Official SDK xplm_WindowLayerFloatingWindows

WindowLayerModal = 2

An interruptive modal that covers the sim with a transparent black overlay to draw the user’s focus to the alert.

Official SDK xplm_WindowLayerModal

WindowLayerGrowlNotifications = 3

“Growl”-style notifications that are visible in a corder of the screen, even over modals.

Official SDK xplm_WindowLayerGrowlNotifications

>>> windowID = xp.createWindowEx(visible=1)
../../_images/small_round_rect_window.png

There are six possible callback functions to be provided.

Callback function signature

Returns

draw(windowID, refCon)

No return

key(windowID, key, flags, vKey, refCon, losingFocus)

No return

cursor(windowID, x, y, refCon)

cursorStatus

click(windowID, x, y, mouseStatus, refCon)

1 =Consume click

0 =Pass it through

wheel(windowID, x, y, wheel, clicks, refCon)

1 =Consume click

0 =Pass it through

rightClick(windowID, x, y, mouseStatus, refCon)

1 =Consume click

0 =Pass it through

For legacy purposes, you may pass a 14-element tuple instead of individually specifying the parameters.

The tuple is:

(
  left, top, right, bottom,
  visible,
  draw,
  click,
  key,
  cursor,
  wheel,
  refCon,
  decoration,
  layer,
  rightClick
)

Note the order is very important!

You pass in a tuple with all of the fields set in.

>>> t = (100, 200, 200, 100, 1,
...      None, None, None, None, None,
...      None,
...      xp.WindowDecorationRoundRectangle, xp.WindowLayerFloatingWindows,
...      None)
...
>>> windowID = xp.createWindowEx(t)
../../_images/small_round_rect_window.png

Official SDK XPLMCreateWindowEx

destroyWindow(windowID)

Destroys a window based on the handle passed in.

The callbacks are not called after this call. Keyboard focus is removed from the window before destroying it.

Official SDK XPLMDestroyWindow

Window Drawing Callbacks

These are the callbacks you’ll provide and pass into createWindowEx() when creating the window, or using the tuple.

draw(windowID, refCon)

Window drawing callback prototype.

Parameters
  • windowID – window to be drawn

  • refCon – refCon you provided on creation

Returns

No return value

This function handles drawing. You are passed in your windowID and its refCon. Draw the window. You can use getWindowGeometry() to find its current dimensions. When this callback is called, the OpenGL context will be set properly for cockpit drawing. NOTE: Because you are drawing your window over a background, you can make a transparent window easily by simply not filling in your entire window’s bounds.

>>> def MyDraw(windowID, refCon):
...     (left, top, right, bottom) = xp.getWindowGeometry(windowID)
...     xp.drawString([1, 0, 0], left + 10, top - 10, refCon, None, xp.Font_Basic)
...
>>> phrase = "Hello"
>>> windowID = xp.createWindowEx(visible=1, draw=MyDraw, refCon=phrase)
>>>
>>> xp.destroyWindow(windowID)
click(windowID, x, y, mouseStatus, refCon)
rightClick(windowID, x, y, mouseStatus, refCon)

Mouse handling callback prototype. Same signature for Left-clicks and Right-clicks. (Note if you do use the same callback for both right and left clicks, you cannot determine from the parameters if you are being called due to a right or left click. For this reason, you might want to use two different functions.)

Parameters
  • windowID – window receiving the mouse click

  • x – horizontal position of mouse

  • y – vertical position of mouse

  • mouseStatus – flag, see table below.

  • refCon – refCon you provided on creation

Returns

1= consume the click, or 0= to pass it through.

You receive this call when the mouse button is pressed down or released. Between then these two calls is a drag. You receive the x and y of the click, your window, and a refcon. Return 1 to consume the click, or 0 to pass it through.

Warning

passing clicks through windows (as of this writing) causes mouse tracking problems in X-Plane; do not use this feature!

When the mouse is clicked, your mouse click routine is called repeatedly. It is first called with the mouse down message. It is then called zero or more times with the mouse-drag message, and finally it is called once with the mouse up message. All of these messages will be directed to the same window.

>>> def MyDraw(windowID, refCon):
...     if refCon:
...        (left, top, right, bottom) = xp.getWindowGeometry(windowID)
...        xp.drawString([1, 0, 0], left + 10, top - 10, refCon, None, xp.Font_Basic)
...
>>> def MyClick(windowID, x, y, mouseStatus, refCon):
...     status = "Down" if mouseStatus == xp.MouseDown else "Up" if mouseStatus == xp.MouseUp else "Drag"
...     xp.setWindowRefCon(windowID, f"({x}, {y}): {status}")
...     return 1  # Remember to return a value!!
...
>>> windowID = xp.createWindowEx(visible=1, click=MyClick, draw=MyDraw)
>>>
>>> xp.destroyWindow(windowID)

Mouse Status Value

SDK Value

MouseDown = 1

xplm_MouseDown

MouseDrag = 2

xplm_MouseDrag

MouseUp = 3

xplm_MouseUp

key(windowID, key, flags, vKey, refCon, losingFocus)

Window keyboard input handling callback prototype.

Parameters
  • windowID – window receiving the key press or focus

  • key – Key pressed

  • flags – OR’d values for Shift / Ctrl, etc. See table below

  • vKey – Virtual key code (Virtual Key Codes)

  • refCon – refCon you provided on creation

  • losingFocus – 1= your window is losing keyboard focus (and key should be ignored)

Returns

No return value

This function is called when a key is pressed or keyboard focus is taken away from your window. If losingFocus is 1, you are losing the keyboard focus, otherwise a key was pressed and key contains its character.

>>> def MyDraw(windowID, refCon):
...     if refCon:
...        (left, top, right, bottom) = xp.getWindowGeometry(windowID)
...        xp.drawString([1, 0, 0], left + 10, top - 10, refCon, None, xp.Font_Basic)
...
>>> def MyKey(windowID, key, flags, vKey, refCon, losingFocus):
...     if losingFocus:
...         status = "Lost Focus"
...     elif flags & xp.DownFlag:
...         status = ['Key Down', ]
...         if flags & xp.ShiftFlag:
...            status.append("Shift")
...         if flags & xp.OptionAltFlag:
...            status.append("Option")
...         if flags & xp.ControlFlag:
...            status.append("Control")
...         status.append(xp.getVirtualKeyDescription(vKey))
...         status = ' '.join(status)
...     elif flags & xp.UpFlag:
...         status = "Key Up"
...     else:
...         return 1  # status unchanged
...     xp.setWindowRefCon(windowID, status)
...     return 1  # Remember to return a value!!
...
>>> windowID = xp.createWindowEx(visible=1, key=MyKey, draw=MyDraw)
>>> xp.takeKeyboardFocus(windowID)  # (because the debugger has focus right now)
>>>
>>> xp.destroyWindow(windowID)

Key Flags Value

SDK Value

ShiftFlag

xplm_ShiftFlag

OptionAltFlag

xplm_OptionAltFlag

ControlFlag

xplm_ControlFlag

DownFlag

xplm_DownFlag

UpFlag

xplm_UpFlag

Warning

X-Plane sends the wrong windowID when losingFocus is set. We’re supposed to get the windowID of the losing window, instead we get the windowID of the window gaining focus. The problem is, we cannot determine which window is actually losing focus & therefore cannot forward this “losing” message to the correct window’s key() callback function. Bug filed with Laminar 22-October-2021. As this will require a re-work of the X-Plane API to actually fix it, there is no time line on this.

As a result, you will never receive a callback with losingFocus=1.

cursor(windowID, x, y, refCon)

Mouse cursor handling callback prototype.

Parameters
  • windowID – window receiving the notice

  • x – horizontal position of mouse

  • y – vertical position of mouse

  • refCon – refCon you provided on creation

Returns

Cursor status (see below)

The SDK calls your cursor status callback when the mouse is over your plugin window. Return a cursor status code to indicate how you would like X-Plane to manage the cursor. If you return CursorDefault, the SDK will try lower-Z-order plugin windows, then let the sim manage the cursor.

Note

you should never show or hide the cursor yourself using non-X-Plane routines as these APIs are typically reference-counted and thus cannot safely and predictably be used by the SDK. Instead return one of CursorHidden to hide the cursor or CursorArrow/CursorCustom to show the cursor.

>>> def MyCursor(windowID, x, y, refCon):
...     (left, top, right, bottom) = xp.getWindowGeometry(windowID)
...     # Arrow, if on the left half of window, Hide if on the right
...     if x > left and x < (right + left) / 2:
...          return xp.CursorArrow
...     else:
...          return xp.CursorHidden
...
>>> windowID = xp.createWindowEx(visible=1, cursor=MyCursor)
>>>
>>> xp.destroyWindow(windowID)

If you want to implement a custom cursor by drawing a cursor in OpenGL, use CursorHidden to hide the OS cursor and draw the cursor using a 2-d drawing callback (after Phase_Window is probably a good choice).

If you want to use a custom OS-based cursor, return CursorCustom to ask X-Plane to show the cursor but not affect its image. You can then use an OS specific call like SetThemeCursor (Mac) or SetCursor/LoadCursor (Windows). [If you can figure out how to actually do this in python3, let me know, so I can provide an example!]

XPLMCursorStatus

Cursor Status Value

SDK Value

CursorDefault = 0

X-Plane manages the cursor normally, plugins does not affect the cursor.

Official SDK xplm_CursorDefault

CursorHidden = 1

X-Plane hides the cursor.

Official SDK xplm_CursorHidden

CursorArrow = 2

X-Plane shows the cursor as the default arrow.

Official SDK xplm_CursorArrow

CursorCustom = 3

X-Plane shows the cursor but lets you select an OS cursor.

Official SDK xplm_CursorCustom

wheel(windowID, x, y, click, refCon)

Mouse wheel handling callback prototype.

Parameters
  • windowID – window receiving the mouse event

  • x – horizontal position of mouse

  • y – vertical position of mouse

  • wheel – 0= vertical axis, 1= horizontal axis

  • clicks – number of “clicks” indicating how far the wheel has turned since previous callback

  • refCon – refCon you provided on creation

Returns

1= consume the mouse wheel click, 0= pass to lower window

The SDK calls your mouse wheel callback when one of the mouse wheels is turned within your window. Return 1 to consume the mouse wheel clicks or 0 to pass them on to a lower window. (You should consume mouse wheel clicks even if they do nothing, if your window appears opaque to the user.) The number of clicks indicates how far the wheel was turned since the last callback. The wheel is 0 for the vertical axis or 1 for the horizontal axis (for OS/mouse combinations that support this).

The units for x and y values matches the units used in your window (i.e., boxels), with origin in lower left of global desktop space.

Screen and Monitor Functions

See detailed explanation of screens vs. monitors and positioning windows within bounds in More about Window Positioning.

getScreenSize()

Query X-Plane screen size. This routine returns the size of the size of the X-Plane OpenGL window in pixels. Please note that this is not the size of the screen when doing 2-d drawing (the 2-d screen is currently always 1024x768, and graphics are scaled up by OpenGL when doing 2-d drawing for higher-res monitors). This number can be used to get a rough idea of the amount of detail the user will be able to see when drawing in 3-d.

Returns (width, height).

>>> xp.getScreenSize()
(1280, 1024)

Official SDK XPLMGetScreenSize

getScreenBoundsGlobal()

This routine returns the bounds of the “global” X-Plane desktop, in boxels. Unlike the non-global version getScreenSize(), this is multi-monitor aware. There are three primary consequences of multimonitor awareness:

  • First, if the user is running X-Plane in full-screen on two or more monitors (typically configured using one full-screen window per monitor), the global desktop will be sized to include all X-Plane windows.

  • Second, the origin of the screen coordinates is not guaranteed to be (0, 0). Suppose the user has two displays side- by-side, both running at 1080p. Suppose further that they’ve configured their OS to make the left display their “primary” monitor, and that X-Plane is running in full-screen on their right monitor only. In this case, the global desktop bounds would be the rectangle from (1920, 0) to (3840, 1080). If the user later asked X-Plane to draw on their primary monitor as well, the bounds would change to (0, 0) to (3840, 1080).

  • Finally, if the usable area of the virtual desktop is not a perfect rectangle (for instance, because the monitors have different resolutions or because one monitor is configured in the operating system to be above and to the right of the other), the global desktop will include any wasted space. Thus, if you have two 1080p monitors, and monitor 2 is configured to have its bottom left touch monitor 1’s upper right, your global desktop area would be the rectangle from (0, 0) to (3840, 2160).

Note that popped-out windows (windows drawn in their own operating system windows, rather than “floating” within X-Plane) are not included in these bounds.

Return (left, top, right, bottom)

>>> xp.getScreenBoundsGlobal()
(-1280, 1440, 2560, 0)

Official SDK XPLMGetScreenBoundsGlobal

getAllMonitorBoundsGlobal(bounds, refCon)

This routine immediately calls your bounds() function the bounds (in boxels) of each full-screen X-Plane window within the X- Plane global desktop space. Note that if a monitor is not covered by an X-Plane window, you cannot get its bounds this way. Likewise, monitors with only an X-Plane window (not in full-screen mode) will not be included.

If X-Plane is running in full-screen and your monitors are of the same size and configured contiguously in the OS, then the combined global bounds of all full-screen monitors will match the total global desktop bounds, as returned by getScreenBoundsGlobal(). (Of course, if X-Plane is running in windowed mode, this will not be the case. Likewise, if you have differently sized monitors, the global desktop space will include wasted space.)

Note that this function’s monitor indices match those provided by getAllMonitorBoundsOS(), but the coordinates are different (since the X-Plane global desktop may not match the operating system’s global desktop, and one X-Plane boxel may be larger than one pixel due to 150% or 200% scaling).

>>> def MyBounds(index, left, top, right, bottom, refCon):
...     refCon[index] = (left, top, right, bottom)
...
>>> data = {}
>>> xp.getAllMonitorBoundsGlobal(MyBounds, data)
>>> data
{0: (0, 1440, 2560, 0), 2: (-1280, 1368, 0 344)}

(In the above example, I have two of my three monitors running fullscreen.)

This function is informed of the global bounds (in boxels) of a particular monitor within the X-Plane global desktop space. Note that X-Plane must be running in full screen on a monitor in order for that monitor to be passed to you in this callback.

getAllMonitorBoundsOS(bounds, refCon)

This routine immediately calls your bounds() function with the bounds (in pixels) of each monitor within the operating system’s global desktop space. Note that unlike getAllMonitorBoundsGlobal(), this may include monitors that have no X-Plane window on them.

Note that this function’s monitor indices match those provided by getAllMonitorBoundsGlobal(), but the coordinates are different (since the X-Plane global desktop may not match the operating system’s global desktop, and one X-Plane boxel may be larger than one pixel).

>>> def MyBoundsOS(index, left, top, right, bottom, refCon):
...     refCon[index] = (left, top, right, bottom)
...
>>> data = {}
>>> xp.getAllMonitorBoundsOS(MyBoundsOS, data)
>>> data
{0: (0, 1440, 2560, 0), 1: (2560, 1840, 3760, -80), 2: (-1280, 1368, 0, 344)}

This function is informed of the global bounds (in pixels) of a particular monitor within the operating system’s global desktop space. Note that a monitor index being passed to you here does not indicate that X-Plane is running in full screen on this monitor, or even that any X-Plane windows exist on this monitor.

Official SDK XPLMGetAllMonitorBoundsOS

getMouseLocationGlobal()

Returns the current mouse location in global desktop boxels. Unlike older getMouseLocation(), the bottom left of the main X-Plane window is not guaranteed to be (0, 0). Instead, the origin is the lower left of the entire global desktop space. In addition, this routine gives the real mouse location when the mouse goes to X-Plane windows other than the primary display. Thus, it can be used with both pop-out windows and secondary monitors.

This is the mouse location function to use with modern windows (i.e., those created by createWindowEx()).

Returns (x, y)

>>> xp.getMouseLocationGlobal()
(3025, 204)

Official SDK XPLMGetMouseLocationGlobal

Window Functions

getWindowGeometry(windowID)

This routine returns the position and size of a window. The units and coordinate system vary depending on the type of window you have.

If this is a legacy window (one compiled against a pre-XPLM300 version of the SDK, or an XPLM300 window that was not created using createWindowEx()), the units are pixels relative to the main X-Plane display.

For X-Plane 11 and 12 windows (compiled against the XPLM300 SDK and created using createWindowEx()), the units are global desktop boxels.

Returns (left, top, right, bottom)

>>> windowID = xp.createWindowEx()
>>> xp.getWindowGeometry(windowID)
(100, 200, 200, 100)

Note that a window has geometry even when not visible & createWindowEx() creates hidden windows by default. Make it visible using xp.setWindowIsVisible(windowID).

Also supports older calling style where you pass in lists as parameters, the results are copied rather than returned. (Don’t use this – it’s here really just to help those used to the way C-language SDK worked.)

>>> left = []; right = []; top = []; bottom = [];
>>> xp.getWindowGeometry(windowID, left, top, right, bottom)
>>> left[0]
100
>>> top[0]
200

Official SDK XPLMGetWindowGeometry

setWindowGeometry(windowID, left, top, right, bottom)

Set window position and size.

This routine allows you to set the position and size of a window.

The units and coordinate system match those of getWindowGeometry(). That is, modern windows use global desktop boxel coordinates, while legacy windows use pixels relative to the main X-Plane display.

Note that this only applies to “floating” windows (that is, windows that are drawn within the X-Plane simulation windows, rather than being “popped out” into their own first-class operating system windows). To set the position of windows whose positioning mode is WindowPopOut, you’ll need to instead use setWindowGeometryOS().

>>> windowID = xp.createWindowEx()
>>> xp.getWindowGeometry(windowID)
(100, 200, 200, 100)
>>> xp.setWindowGeometry(windowID, 200, 300, 400, 250)
>>> xp.getWindowGeometry(windowID)
(200, 300, 400, 250)

Official SDK XPLMSetWindowGeometry

getWindowGeometryOS(windowID)

This routine returns the position and size of a “popped out” window (i.e., a window whose positioning mode is WindowPopOut), in operating system pixels. If the window is not popped out, do not use.

Returns (left, top, right, bottom)

>>> windowID = xp.createWindowEx()
>>> xp.setWindowPositioningMode(windowID, xp.WindowPopOut, -1)
>>> xp.getWindowGeometryOS(windowID)
(90 610, 210, 490)

Also supports alternate calling style where you can pass lists as parameters (see example with getWindowGeometry().

Official SDK XPLMGetWindowGeometryOS

setWindowGeometryOS(windowID, left, top, right, bottom)

This routine allows you to set the position and size, in operating system pixel coordinates, of a popped out window (that is, a window whose positioning mode is WindowPopOut, which exists outside the X-Plane simulation window, in its own first-class operating system window).

Note that you are responsible for ensuring both that your window is popped out (using windowIsPoppedOut()) and that a monitor really exists at the OS coordinates you provide (using getAllMonitorBoundsOS()).

Official SDK XPLMSetWindowGeometryOS

getWindowGeometryVR(windowID)

Returns the width and height, in boxels, of a window in VR. Note that you are responsible for ensuring your window is in VR (using windowIsInVR()).

Return (widthBoxels, heightBoxels)

>>> windowID = xp.createWindowEx()
>>> if xp.windowIsInVR(windowID):
...     xp.getWindowGeometryVR(windowID)
...
(200, 100)

Official SDK XPLMGetWindowGeometryVR

setWindowGeometryVR(windowID, width, height)

This routine allows you to set the size, in boxels, of a window in VR (that is, a window whose positioning mode is WindowVR).

Note that you are responsible for ensuring your window is in VR (using windowIsInVR()).

Official SDK XPLMSetWindowGeometryVR

getWindowIsVisible(windowID)

Get window’s isVisible attribute value, 1 if visible, 0 otherwise.

>>> windowID = xp.createWindowEx()
>>> xp.getWindowIsVisible(windowID)
0

Official SDK XPLMGetWindowIsVisible

setWindowIsVisible(windowID, visible=1)

Set window’s visible attribute value, 1 for visible, 0 otherwise.

Official SDK XPLMSetWindowIsVisible

windowIsPoppedOut(windowID)

True if this window has been popped out (making it a first-class window in the operating system), which in turn is true if and only if you have set the window’s positioning mode to WindowPopOut.

Only applies to modern windows. (Windows created using the deprecated XPLMCreateWindow(), or windows compiled against a pre-XPLM300 version of the SDK cannot be popped out.)

Returns 1 if window is popped out.

Official SDK XPLMWindowIsPoppedOut

windowIsInVR(windowID)

True if this window has been moved to the virtual reality (VR) headset, which in turn is true if and only if you have set the window’s positioning mode to WindowVR.

Only applies to modern windows. (Windows created using the deprecated XPLMCreateWindow(), or windows compiled against a pre-XPLM301 version of the SDK cannot be moved to VR.)

Return 1 if window is in VR, 0 otherwise.

Official SDK XPLMWindowIsInVR

setWindowGravity(inWindowID, left, top, right, bottom) None:

A window’s “gravity” controls how the window shifts as the whole X-Plane window resizes. A gravity of 1 means the window maintains its positioning relative to the right or top edges, 0 the left/bottom, and 0.5 keeps it centered.

Default gravity is (0.0, 1.0, 0.0, 1.0), meaning your window will maintain its position relative to the top left and will not change size as its containing window grows. (That is, the right and bottom edges of your window will also maintain relative positions to top left.)

(0.0, 1.0, 0.0, 1.0) can be interpreted as:

value

Refers to your…

Resulting in…

0.0

left edge of your window

“0.0” means it will maintain position relative to left of screen

1.0

top edge of your window

“1.0” means it will maintain position relative to top of screen

0.0

right edge of your window,

“0.0” means it will maintain position relative to left of screen

1.0

bottom edge of your window

“1.0” means it will maintain position relative to top of screen

Therefore your window will keep its size, and the upper left of your window will stay in the same location (i.e., same number of pixels from down from the top and over from the left of the screen.(

(0.0, 1.0, 1.0, 1.0) can be interpreted as:

value

Refers to your…

Resulting in…

0.0

left edge of your window

“0.0” means it will maintain position relative to left of screen

1.0

top edge of your window

“1.0” means it will maintain position relative to top of screen

1.0

right edge of your window,

“1.0” means it will maintain position 100% relative to right

1.0

bottom edge of your window

“1.0” means it will maintain position relative to top of screen

As before, this will keep upper left edges where they are, bottom remains same distance from top (so the window remains same height). However, right edge of your window will track the right side of your screen: if your screen increases 100, your window’s right edge expands 100%, or 100 pixels.

(0.0, 1.0, 0.5, 1.0) can be interpreted as:

value

Refers to your…

Resulting in…

0.0

left edge of your window

“0.0” means it will maintain position relative to left of screen

1.0

top edge of your window

“1.0” means it will maintain position relative to top of screen

0.5

right edge of your window,

“0.5” means it will maintain position 50% relative to right

1.0

bottom edge of your window

“1.0” means it will maintain position relative to top of screen

Same as previous but right edge expands 50% of the change in screen width: If your screen increases 100, your window’s right edge expands 50)

Only applies to modern windows. (Windows created using the deprecated older XPLMCreateWindow(), or windows compiled against a pre-XPLM300 version of the SDK will simply get the default gravity.)

Official SDK XPLMSetWindowGravity

setWindowResizingLimits(windowID, minWidth=0, minHeight=0, maxWidth=10000, maxHeight=10000)

Sets the minimum and maximum size of the client rectangle of the given window. (That is, it does not include any window styling that you might have asked X-Plane to apply on your behalf.) All resizing operations are constrained to these sizes. (Except see Note below.)

Only applies to modern windows. (Windows created using the deprecated XPLMCreateWindow(), or windows compiled against a pre-XPLM300 version of the SDK will have no minimum or maximum size.)

This is especially useful if you’ve set window gravity (setWindowGravity() such that the height or width of the window changes as the screen changes.

Note

setWindowGravity() may cause the window to expand exceeding maxWidth, maxHeight values for setWindowResizingLimits(), any future changes to that window’s geometry (either via manual dragging or setWindowGeometry()) will cause the window to snap to its declared sizing limit, but until the window is resized, it will remain too large. Bug filed with Laminar 23-October-2021 as XPD-11455. Minimum sizes are correctly constrained.

Official SDK XPLMSetWindowResizingLimits

>>> windowID = xp.createWindowEx(visible=1, left=100, right=200)
>>> xp.setWindowResizingLimits(windowID, minWidth=100)
setWindowPositioningMode(windowID, mode, index=-1)

Sets the policy for how X-Plane will position your window.

Some positioning modes apply to a particular monitor. For those modes, you can pass a negative monitor index to position the window on the main X-Plane monitor (the screen with the X-Plane menu bar at the top). Or, if you have a specific monitor you want to position your window on, you can pass a real monitor index as received from, e.g., getAllMonitorBoundsOS().

Only applies to modern windows. (Windows created using the deprecated XPLMCreateWindow(), or windows compiled against a pre-XPLM300 version of the SDK will always use xplm_WindowPositionFree.)

The mode describes how X-Plane will position your window on the user’s screen. X-Plane will maintain this positioning mode even as the user resizes their window or adds/removes full-screen monitors.

Official SDK XPLMSetWindowPositioningMode

>>> windowID = xp.createWindowEx(visible=1)
>>> xp.setWindowPositioningMode(windowID, xp.WindowPopOut)

XPLMWindowPositioningMode

Positioning Mode Value

Meaning

WindowPositionFree = 0

The default positioning mode. Set the window geometry and its future position will be determined by its window gravity, resizing limits, and user interactions.

Official SDK xplm_WindowPositionFree

WindowCenterOnMonior = 1

Keep the window centered on the monitor you specify

Official SDK xplm_WindowCenterOnMonitor

WindowFullScreenOnMonitor = 2

Keep the window full screen on the monitor you specify

Official SDK xplm_WindowFullScreenOnMonitor

WindowFullScreenOnAllMonitors = 3

Like gui_window_full_screen_on_monitor, but stretches over all monitors and popout windows. This is an obscure one… unless you have a very good reason to need it, you probably don’t!

Official SDK xplm_WindowFullScreenOnAllMonitors

WindowPopOut = 4

A first-class window in the operating system, completely separate from the X-Plane window(s)

Official SDK xplm_WindowPopOut

WindowVR = 5

A floating window visible on the VR headset

Official SDK xplm_WindowVR

setWindowTitle(windowID, title)

Sets the name for a window. This only applies to windows that opted-in to styling as an X-Plane 11 floating window (i.e., with styling mode xplm_WindowDecorationRoundRectangle) when they were created using XPLMCreateWindowEx().

>>> windowID = xp.createWindowEx(visible=1)
>>> xp.setWindowTitle(windowID, "New Title")

Official SDK: XPLMSetWindowTitle

getWindowRefCon(windowID)

Return window’s refCon attribute value (which you provided on window creation.)

>>> windowID = xp.createWindowEx(visible=1)
>>> xp.getWindowRefCon(windowID)
None

Official SDK: XPLMGetWindowRefCon

setWindowRefCon(windowID, refCon)

Set window’s refcon attribute value. Use this to pass data to yourself in the callbacks.

>>> windowID = xp.createWindowEx(visible=1)
>>> xp.getWindowRefCon(windowID)
None
>>> xp.setWindowRefCon(windowID, {"data": "value"})
>>> xp.getWindowRefCon(windowID, {"data": "value"})
{"data": "value"}

Official SDK: XPLMSetWindowRefCon

takeKeyboardFocus(windowID)

Give a specific window keyboard focus.

This routine gives a specific window keyboard focus. Keystrokes will be sent to that window. Pass a window ID of 0 to remove keyboard focus from any plugin-created windows and instead pass keyboard strokes directly to X-Plane.

>>> windowID = xp.createWindowEx(visible=1)
>>> xp.hasKeyboardFocus(windowID)
0
>>> xp.takeKeyboardFocus(windowID) ; xp.hasKeyboardFocus(windowID)
1

(Because the debugger will have keyboard focus, for obvious reasons, execute take and has on the same line to see the above results.)

Official SDK: XPLMTakeKeyboardFocus

hasKeyboardFocus(windowID)

Returns 1 if the indicated window has keyboard focus. Pass a window ID of 0 to see if no plugin window has focus, and all keystrokes will go directly to X-Plane.

Official SDK: XPLMHasKeyboardFocus

bringWindowToFront(windowID)

Bring window to the front of the Z-order.

This routine brings the window to the front of the Z-order for its layer. Windows are brought to the front automatically when they are created. Beyond that, you should make sure you are front before handling mouse clicks.

Note that this only brings your window to the front of its layer XPLMWindowLayer. Thus, if you have a window in the floating window layer (WindowLayerFloatingWindows), but there is a modal window (in layer WindowLayerModal) above you, you would still not be the true frontmost window after calling this. (After all, the window layers are strictly ordered, and no window in a lower layer can ever be above any window in a higher one.) Windows are brought to the front when they are created. Beyond that you should make sure you are front before handling mouse clicks.

Official SDK: XPLMBringWindowToFront

isWindowInFront(windowID)

This routine returns 1 if the window you passed in is the frontmost visible window in its layer Window layer.

Thus, if you have a window at the front of the floating window layer (WindowLayerFloatingWindows), this will return true even if there is a modal window (in layer WindowLayerModal) above you. (Not to worry, though: in such a case, X-Plane will not pass clicks or keyboard input down to your layer until the window above stops “eating” the input.)

Note that legacy windows are always placed in layer WindowLayerFlightOverlay, while modern-style windows default to WindowLayerFloatingWindows. This means it’s perfectly consistent to have two different plugin-created windows (one legacy, one modern) both be in the front (of their different layers!) at the same time.

Official SDK: XPLMIsWindowInFront

Key Sniffing

registerKeySniffer(sniffer, before=0, refCon=None)

This routine registers a key sniffer callback. You specify whether you want to sniff before the window system (before=1), or only sniff keys the window system does not consume (before=0). You should ALMOST ALWAYS sniff non-control keys after the window system. When the window system consumes a key, it is because the user has “focused” a window. Consuming the key or taking action based on the key will produce very weird results.

A window-based UI should not use this! The windowing system provides high-level mediated keyboard access, via the callbacks you attach on window creation createWindowEx(). By comparison, the key sniffer provides low level keyboard access.

Key sniffers are provided to allow libraries to provide non-windowed user interaction. For example, the MUI library uses a key sniffer to do pop-up text entry.

Returns 1 if successful.

Your sniffer callback takes four parameters (key, flags, vKey, refCon). key is the key code, this is OS dependent. In most cases, you should use the vKey (Virtual Key Codes) which, in combination with flags (XPLMKeyFlags), will indicate which key was pressed and if that included Shirt, Control, etc.

Have your sniffer callback return 1 to pass the key on to the next sniffer, the window manager, X-Plane, or whomever is down stream. Return 0 to consume the key.

>>> def MySniffer(key, flags, vKey, refCon):
...     if vKey == xp.VK_Z and flags & xp.ShiftFlag:
...         xp.speakString("You pressed Z")
...         return 0
...     return 1
...
>>> xp.registerKeySniffer(MySniffer)
1
>>> xp.unregisterKeySniffer(MySniffer)
1

Official SDK XPLMRegisterKeySniffer

unregisterKeySniffer(sniffer, before=0, refCon=None)

This routine unregisters a key sniffer. You must unregister a key sniffer for every time you register one with the exact same signature. Returns 1 if successful.

Official SDK XPLMUnregisterKeySniffer

Hot Keys

Keystrokes that can be managed by others. These are lower-level than window keyboard handlers.

If you have a sniffer and a hot key, the sniffer is called first (even if it is an “after” sniffer) and if it consumes the key, the hot key will not be called.

registerHotKey(vKey, flags, description="", hotKey, refCon=None)

Register a hotkey.

vKey (Virtual Key Codes) is the hot key to be pressed to activate (this may be changed later by your plugin, or some other plugin, using setHotKeyCombination()).

flags are bitwise OR’d values for Shift / Ctrl to be pressed with the hot key. Note you need to include xp.DownFlag or xp.UpFlag. (XPLMKeyFlags)

Include a description for the hot key, so others (using getHotKeyInfo()) can understand the intent of your hot key.

You hotKey callback receives only the refCon.

Registration returns a hotKeyID, which is what you’ll use with unRegisterHotKey().

During execution, the actual key associated with your hot key may change, but you are insulated from this.

Official SDK XPLMRegisterHotKey

>>> def MyHotKey(refCon):
...     xp.speakString("You pressed the Hot Key")
...
>>> hotKeyID = xp.registerHotKey(xp.VK_Z, xp.DownFlag, "Speak Hotkey Example", MyHotKey)
>>>
>>> xp.unregisterHotKey(hotKeyID)
unregisterHotKey(hotKeyID)

Unregister a hotkey using hotKeyID you received using registerHotKey().

Only your own hotkeys can be unregistered (even though you can get hotKeyIDs of other Hot Keys using getNthHotKey().)

Official SDK XPLMUnregisterHotKey

countHotKeys()

Return number of hotkeys defined in the whole sim – not just those you defined..

Official SDK XPLMUnregisterHotKey

getNthHotKey(index)

Returns HotKeyID of Nth hotkey (0-based indexing).

>>> xp.countHotKeys()
1
>>> hotKeyID = xp.getNthHotKey(0)

Official SDK XPLMGetNthHotKey

getHotKeyInfo(hotKeyID)

Return information about the hotkey as an object with attributes.

description: str
virtualKey: int (Virtual Key Codes)
flags: int (XPLMKeyFlags)
plugin: int (XPLMPluginID)
>>> i.description
"Speak Hotkey Example"
>>> i.virtualKey
90
>>> i.flags
0
>>> i.plugin
3

Official SDK XPLMGetHotKeyInfo

Note

All python-based hotkeys report the XPPython3 plugin ID: there is a convoluted way (from python) to determine which python plugin created a hotkey, but non-python plugins will always see all python hot keys as originating with the XPPython3 plugin.

setHotKeyCombination(hotKeyID, vKey, flags)

Remap a hot key’s keystroke.

hotKeyID can be either one returned from registerHotKey(), or found using getNthHotKey().

Set vKey and flags as you would with registerHotKey().

You may remap another plugin’s keystrokes. For example, to change the first hot key to the same key, but requiring a Shift:

>>> hotKeyID = xp.getNthHotKey(0)
>>> info = xp.getHotKeyInfo(hotKeyID)
>>> xp.setHotKeyCombination(hotKeyID, info.virtualKey, flags=info.flags | xp.ShiftFlag)

Official SDK XPLMGetHotKeyInfo