flybywireless-xpmp2-sys 0.1.4

Raw FFI over XPMP2 for X-Plane multiplayer aircraft
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
#include "shim.h"

#include <cstring>

#include "XPMPAircraft.h"

namespace {

/// The one concrete `XPMP2::Aircraft` subclass this crate ever needs —
/// `UpdatePosition` forwards into a plain C function pointer + refcon
/// instead of being overridden per-callsite in C++, so every plane created
/// through this shim, regardless of what Rust code drives it, shares this
/// single class.
class ShimAircraft final : public XPMP2::Aircraft {
public:
    XPMP2ShimUpdatePositionFn updatePositionFn = nullptr;
    void* refcon = nullptr;

    ShimAircraft(const std::string& icaoType,
                 const std::string& icaoAirline,
                 const std::string& livery,
                 XPMPPlaneID modeSId,
                 const std::string& cslId)
        : XPMP2::Aircraft(icaoType, icaoAirline, livery, modeSId, cslId)
    {}

    void UpdatePosition(float elapsedSinceLastCall, int flCounter) override
    {
        if (updatePositionFn) {
            updatePositionFn(refcon, elapsedSinceLastCall, flCounter);
        }
    }
};

inline ShimAircraft* cast(XPMP2ShimAircraft* p) { return reinterpret_cast<ShimAircraft*>(p); }
inline const ShimAircraft* cast(const XPMP2ShimAircraft* p) { return reinterpret_cast<const ShimAircraft*>(p); }

inline const char* orEmpty(const char* s) { return s ? s : ""; }

/// Copies `s` into `buf` (up to `buf_len - 1` bytes, NUL-terminated), same
/// truncation contract as BSD `strlcpy`. `buf`/`buf_len` of `0`/`NULL` is a
/// no-op. Returns `s.size()` either way, so callers can detect truncation by
/// comparing the return value against `buf_len`.
size_t copyToBuf(const std::string& s, char* buf, size_t buf_len)
{
    if (buf && buf_len > 0) {
        size_t n = s.size() < buf_len - 1 ? s.size() : buf_len - 1;
        std::memcpy(buf, s.data(), n);
        buf[n] = '\0';
    }
    return s.size();
}

/// `XPMPInfoTexts_t`'s fields are fixed-size `char[]` arrays, not
/// `std::string` — copies `s` in, truncating to fit, always NUL-terminated.
template <size_t N>
void copyToField(const char* s, char (&field)[N])
{
    size_t n = std::strlen(s);
    if (n > N - 1) n = N - 1;
    std::memcpy(field, s, n);
    field[n] = '\0';
}

} // namespace

extern "C" {

XPMP2ShimAircraft* xpmp2_shim_aircraft_create(
    const char* icao_type,
    const char* icao_airline,
    const char* livery,
    unsigned int mode_s_id,
    const char* csl_id,
    XPMP2ShimUpdatePositionFn update_position,
    void* refcon)
{
    // XPMP2::Aircraft's constructor throws XPMP2::XPMP2Error on failure
    // (invalid/duplicate mode_s_id, no CSL model matched) — caught here so
    // no C++ exception ever unwinds across the extern "C" boundary into
    // Rust, which would be undefined behavior (the mirror image of why
    // every Rust-side trampoline in this workspace goes through
    // `xplm::guard()`).
    try {
        auto* a = new ShimAircraft(
            orEmpty(icao_type),
            orEmpty(icao_airline),
            orEmpty(livery),
            static_cast<XPMPPlaneID>(mode_s_id),
            orEmpty(csl_id)
        );
        a->updatePositionFn = update_position;
        a->refcon = refcon;
        return reinterpret_cast<XPMP2ShimAircraft*>(a);
    } catch (...) {
        return nullptr;
    }
}

void xpmp2_shim_aircraft_destroy(XPMP2ShimAircraft* aircraft)
{
    delete cast(aircraft);
}

unsigned int xpmp2_shim_aircraft_mode_s_id(const XPMP2ShimAircraft* aircraft)
{
    return static_cast<unsigned int>(cast(aircraft)->GetModeS_ID());
}

int xpmp2_shim_aircraft_is_valid(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->IsValid() ? 1 : 0;
}

void xpmp2_shim_aircraft_set_visible(XPMP2ShimAircraft* aircraft, int visible)
{
    cast(aircraft)->SetVisible(visible != 0);
}

int xpmp2_shim_aircraft_is_visible(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->IsVisible() ? 1 : 0;
}

void xpmp2_shim_aircraft_set_location(XPMP2ShimAircraft* aircraft, double lat, double lon, double alt_ft)
{
    cast(aircraft)->SetLocation(lat, lon, alt_ft);
}

void xpmp2_shim_aircraft_get_location(const XPMP2ShimAircraft* aircraft, double* out_lat, double* out_lon, double* out_alt_ft)
{
    double lat = 0, lon = 0, alt_ft = 0;
    // GetLocation is non-const in some XPMP2 versions' signature but does
    // not logically mutate; cast away constness locally rather than widen
    // this shim function's own contract.
    const_cast<ShimAircraft*>(cast(aircraft))->GetLocation(lat, lon, alt_ft);
    if (out_lat) *out_lat = lat;
    if (out_lon) *out_lon = lon;
    if (out_alt_ft) *out_alt_ft = alt_ft;
}

void xpmp2_shim_aircraft_set_local_loc(XPMP2ShimAircraft* aircraft, float x, float y, float z)
{
    cast(aircraft)->SetLocalLoc(x, y, z);
}

void xpmp2_shim_aircraft_set_pitch(XPMP2ShimAircraft* aircraft, float degrees)
{
    cast(aircraft)->SetPitch(degrees);
}

void xpmp2_shim_aircraft_set_heading(XPMP2ShimAircraft* aircraft, float degrees)
{
    cast(aircraft)->SetHeading(degrees);
}

void xpmp2_shim_aircraft_set_roll(XPMP2ShimAircraft* aircraft, float degrees)
{
    cast(aircraft)->SetRoll(degrees);
}

void xpmp2_shim_aircraft_set_on_ground(XPMP2ShimAircraft* aircraft, int on_ground)
{
    cast(aircraft)->bOnGrnd = (on_ground != 0);
}

void xpmp2_shim_aircraft_set_velocity(XPMP2ShimAircraft* aircraft, float vx, float vy, float vz)
{
    auto* a = cast(aircraft);
    a->v_x = vx;
    a->v_y = vy;
    a->v_z = vz;
}

void xpmp2_shim_aircraft_set_label(XPMP2ShimAircraft* aircraft, const char* label)
{
    cast(aircraft)->label = orEmpty(label);
}

void xpmp2_shim_aircraft_set_label_drawn(XPMP2ShimAircraft* aircraft, int drawn)
{
    cast(aircraft)->bDrawLabel = (drawn != 0);
}

size_t xpmp2_shim_aircraft_dataref_count(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->v.size();
}

float xpmp2_shim_aircraft_get_dataref(const XPMP2ShimAircraft* aircraft, size_t index)
{
    const auto* a = cast(aircraft);
    return index < a->v.size() ? a->v[index] : 0.0f;
}

void xpmp2_shim_aircraft_set_dataref(XPMP2ShimAircraft* aircraft, size_t index, float value)
{
    auto* a = cast(aircraft);
    if (index < a->v.size()) {
        a->v[index] = value;
    }
}

void xpmp2_shim_aircraft_set_radar(XPMP2ShimAircraft* aircraft, long code, int mode)
{
    auto* a = cast(aircraft);
    a->acRadar.code = code;
    a->acRadar.mode = static_cast<XPMPTransponderMode>(mode);
}

void xpmp2_shim_aircraft_set_label_color(XPMP2ShimAircraft* aircraft, float r, float g, float b, float a)
{
    auto* ac = cast(aircraft);
    ac->colLabel[0] = r;
    ac->colLabel[1] = g;
    ac->colLabel[2] = b;
    ac->colLabel[3] = a;
}

void xpmp2_shim_aircraft_set_vert_ofs_ratio(XPMP2ShimAircraft* aircraft, float ratio)
{
    cast(aircraft)->vertOfsRatio = ratio;
}

float xpmp2_shim_aircraft_get_vert_ofs(const XPMP2ShimAircraft* aircraft)
{
    return const_cast<ShimAircraft*>(cast(aircraft))->GetVertOfs();
}

void xpmp2_shim_aircraft_set_gear_deflect_ratio(XPMP2ShimAircraft* aircraft, float ratio)
{
    cast(aircraft)->gearDeflectRatio = ratio;
}

void xpmp2_shim_aircraft_set_clamp_to_ground(XPMP2ShimAircraft* aircraft, int clamp)
{
    cast(aircraft)->bClampToGround = (clamp != 0);
}

void xpmp2_shim_aircraft_set_ai_priority(XPMP2ShimAircraft* aircraft, int priority)
{
    cast(aircraft)->aiPrio = priority;
}

void xpmp2_shim_aircraft_set_render(XPMP2ShimAircraft* aircraft, int render)
{
    cast(aircraft)->SetRender(render != 0);
}

int xpmp2_shim_aircraft_is_rendered(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->IsRendered() ? 1 : 0;
}

int xpmp2_shim_aircraft_tcas_target_idx(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->GetTcasTargetIdx();
}

int xpmp2_shim_aircraft_is_shown_as_tcas_target(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->IsCurrentlyShownAsTcasTarget() ? 1 : 0;
}

int xpmp2_shim_aircraft_is_shown_as_ai(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->IsCurrentlyShownAsAI() ? 1 : 0;
}

int xpmp2_shim_aircraft_show_as_ai_plane(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->ShowAsAIPlane() ? 1 : 0;
}

float xpmp2_shim_aircraft_camera_dist(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->GetCameraDist();
}

float xpmp2_shim_aircraft_camera_bearing(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->GetCameraBearing();
}

float xpmp2_shim_aircraft_ground_speed_kn(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->GetGS_kn();
}

int xpmp2_shim_aircraft_is_related_to(const XPMP2ShimAircraft* aircraft, const char* icao_type)
{
    return cast(aircraft)->IsRelatedTo(orEmpty(icao_type)) ? 1 : 0;
}

int xpmp2_shim_aircraft_is_ground_vehicle(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->IsGroundVehicle() ? 1 : 0;
}

int xpmp2_shim_aircraft_is_glider(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->IsGlider() ? 1 : 0;
}

int xpmp2_shim_aircraft_change_model(
    XPMP2ShimAircraft* aircraft,
    const char* icao_type,
    const char* icao_airline,
    const char* livery)
{
    return cast(aircraft)->ChangeModel(orEmpty(icao_type), orEmpty(icao_airline), orEmpty(livery));
}

int xpmp2_shim_aircraft_rematch_model(XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->ReMatchModel();
}

int xpmp2_shim_aircraft_match_quality(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->GetMatchQuality();
}

size_t xpmp2_shim_aircraft_get_model_name(const XPMP2ShimAircraft* aircraft, char* buf, size_t buf_len)
{
    return copyToBuf(cast(aircraft)->GetModelName(), buf, buf_len);
}

size_t xpmp2_shim_aircraft_get_flight_id(const XPMP2ShimAircraft* aircraft, char* buf, size_t buf_len)
{
    return copyToBuf(cast(aircraft)->GetFlightId(), buf, buf_len);
}

void xpmp2_shim_aircraft_set_info_texts(
    XPMP2ShimAircraft* aircraft,
    const char* tail_num,
    const char* icao_ac_type,
    const char* manufacturer,
    const char* model,
    const char* icao_airline,
    const char* airline,
    const char* flight_num,
    const char* apt_from,
    const char* apt_to)
{
    auto& t = cast(aircraft)->acInfoTexts;
    copyToField(orEmpty(tail_num), t.tailNum);
    copyToField(orEmpty(icao_ac_type), t.icaoAcType);
    copyToField(orEmpty(manufacturer), t.manufacturer);
    copyToField(orEmpty(model), t.model);
    copyToField(orEmpty(icao_airline), t.icaoAirline);
    copyToField(orEmpty(airline), t.airline);
    copyToField(orEmpty(flight_num), t.flightNum);
    copyToField(orEmpty(apt_from), t.aptFrom);
    copyToField(orEmpty(apt_to), t.aptTo);
}

void xpmp2_shim_aircraft_set_wing_span(XPMP2ShimAircraft* aircraft, float meters)
{
    cast(aircraft)->SetWingSpan(meters);
}

void xpmp2_shim_aircraft_set_wing_area(XPMP2ShimAircraft* aircraft, float square_meters)
{
    cast(aircraft)->SetWingArea(square_meters);
}

void xpmp2_shim_aircraft_set_mass(XPMP2ShimAircraft* aircraft, float kg)
{
    cast(aircraft)->SetMass(kg);
}

void xpmp2_shim_aircraft_wake_apply_defaults(XPMP2ShimAircraft* aircraft, int overwrite_all)
{
    cast(aircraft)->WakeApplyDefaults(overwrite_all != 0);
}

void xpmp2_shim_aircraft_contrail_request(XPMP2ShimAircraft* aircraft, unsigned num, unsigned dist_m, unsigned life_time_s)
{
    cast(aircraft)->ContrailRequest(num, dist_m, life_time_s);
}

void xpmp2_shim_aircraft_contrail_remove(XPMP2ShimAircraft* aircraft)
{
    cast(aircraft)->ContrailRemove();
}

unsigned xpmp2_shim_aircraft_contrail_trigger(XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->ContrailTrigger();
}

void xpmp2_shim_aircraft_set_sound_min_dist(XPMP2ShimAircraft* aircraft, int meters)
{
    cast(aircraft)->sndMinDist = meters;
}

void xpmp2_shim_aircraft_set_sound_muted(XPMP2ShimAircraft* aircraft, int mute)
{
    cast(aircraft)->SoundMuteAll(mute != 0);
}

int xpmp2_shim_aircraft_is_sound_muted(const XPMP2ShimAircraft* aircraft)
{
    return cast(aircraft)->SoundIsMuted() ? 1 : 0;
}

} // extern "C"