Skip to content

Commit 5501787

Browse files
committed
macOS: Cleanup platform-specific port implementation
1 parent 33369fb commit 5501787

File tree

1 file changed

+103
-115
lines changed

1 file changed

+103
-115
lines changed

src/platform/psmove_port_osx.mm

Lines changed: 103 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* PS Move API - An interface for the PS Move Motion Controller
3-
* Copyright (c) 2016, 2023 Thomas Perl <[email protected]>
3+
* Copyright (c) 2016, 2023, 2025 Thomas Perl <[email protected]>
44
* All rights reserved.
55
*
66
* Redistribution and use in source and binary forms, with or without
@@ -57,6 +57,8 @@
5757

5858
#define OSXPAIR_DEBUG(...) PSMOVE_INFO(__VA_ARGS__)
5959

60+
namespace {
61+
6062
struct ScopedNSAutoreleasePool {
6163
ScopedNSAutoreleasePool()
6264
: pool([[NSAutoreleasePool alloc] init])
@@ -68,9 +70,32 @@
6870
}
6971

7072
private:
73+
ScopedNSAutoreleasePool(const ScopedNSAutoreleasePool &) = delete;
74+
ScopedNSAutoreleasePool &operator=(const ScopedNSAutoreleasePool &) = delete;
75+
7176
NSAutoreleasePool *pool;
7277
};
7378

79+
std::vector<std::string>
80+
subprocess(const std::string &cmdline)
81+
{
82+
FILE *fp = popen(cmdline.c_str(), "r");
83+
if (!fp) {
84+
return {};
85+
}
86+
87+
std::vector<std::string> result;
88+
char line[1024];
89+
while (fgets(line, sizeof(line), fp)) {
90+
// Remove trailing newline
91+
line[strlen(line)-1] = '\0';
92+
result.emplace_back(line);
93+
}
94+
95+
pclose(fp);
96+
return result;
97+
}
98+
7499
static int
75100
macosx_bluetooth_set_powered(int powered)
76101
{
@@ -95,64 +120,21 @@
95120
return 1;
96121
}
97122

98-
static char *
99-
macosx_get_btaddr()
100-
{
101-
ScopedNSAutoreleasePool pool;
102-
103-
char *result;
104-
105-
macosx_bluetooth_set_powered(1);
106-
107-
IOBluetoothHostController *controller =
108-
[IOBluetoothHostController defaultController];
109-
110-
NSString *addr = [controller addressAsString];
111-
psmove_return_val_if_fail(addr != NULL, NULL);
112-
113-
result = strdup([addr UTF8String]);
114-
psmove_return_val_if_fail(result != NULL, NULL);
115-
116-
char *tmp = result;
117-
while (*tmp) {
118-
if (*tmp == '-') {
119-
*tmp = ':';
120-
}
121-
122-
tmp++;
123-
}
124-
125-
return result;
126-
}
127-
128-
static int
123+
static bool
129124
macosx_blued_running()
130125
{
131-
FILE *fp = popen("ps -axo comm", "r");
132-
char command[1024];
133-
int running = 0;
134-
135-
while (fgets(command, sizeof(command), fp)) {
136-
/* Remove trailing newline */
137-
command[strlen(command)-1] = '\0';
138-
139-
if (strcmp(command, "/usr/sbin/blued") == 0) {
140-
running = 1;
126+
for (const auto &line: subprocess("/bin/ps -axo comm")) {
127+
if (line == "/usr/sbin/blued") {
128+
return true;
141129
}
142130
}
143131

144-
pclose(fp);
145-
146-
return running;
132+
return false;
147133
}
148134

149135
static bool
150136
macosx_blued_is_paired(const std::string &btaddr)
151137
{
152-
FILE *fp = popen("defaults read " OSX_BT_CONFIG_PATH " HIDDevices", "r");
153-
char line[1024];
154-
int found = 0;
155-
156138
/**
157139
* Example output that we need to parse:
158140
*
@@ -164,76 +146,67 @@
164146
*
165147
**/
166148

167-
while (fgets(line, sizeof(line), fp)) {
168-
char *entry = strchr(line, '"');
169-
if (entry) {
170-
entry++;
171-
char *delim = strchr(entry, '"');
172-
if (delim) {
173-
*delim = '\0';
174-
if (strcmp(entry, btaddr.c_str()) == 0) {
175-
found = 1;
149+
for (const auto &line: subprocess("/usr/bin/defaults read " OSX_BT_CONFIG_PATH " HIDDevices")) {
150+
size_t pos = line.find('"');
151+
if (pos != std::string::npos) {
152+
++pos;
153+
size_t rpos = line.rfind('"');
154+
if (rpos != std::string::npos) {
155+
std::string entry = line.substr(pos, rpos - pos);
156+
if (entry == btaddr) {
157+
return true;
176158
}
177159
}
178160
}
179161
}
180162

181-
pclose(fp);
182-
return found;
163+
return false;
183164
}
184165

185-
struct MacOSVersionNumber {
186-
MacOSVersionNumber(int major=-1, int minor=-1) : major(major), minor(minor) {}
187-
188-
bool valid() const { return major != -1 && minor != -1; }
189-
190-
int major;
191-
int minor;
192-
};
166+
struct MacOSVersion {
167+
static MacOSVersion
168+
running()
169+
{
170+
for (const auto &line: subprocess("/usr/bin/sw_vers -productVersion")) {
171+
int major, minor, patch = 0;
172+
int assigned = sscanf(line.c_str(), "%d.%d.%d", &major, &minor, &patch);
173+
174+
/**
175+
* On Mac OS X 10.8.0, the command returns "10.8", so we allow parsing
176+
* only the first two numbers of the triplet, leaving the patch version
177+
* to the default (0) set above.
178+
*
179+
* See: https://github.com/thp/psmoveapi/issues/32
180+
**/
181+
if (assigned == 2 || assigned == 3) {
182+
return MacOSVersion {major, minor};
183+
}
184+
}
193185

194-
bool
195-
operator<(const MacOSVersionNumber &a, const MacOSVersionNumber &b)
196-
{
197-
return (a.major <= b.major && a.minor < b.minor);
198-
}
186+
return MacOSVersion {};
187+
}
199188

200-
bool
201-
operator>=(const MacOSVersionNumber &a, const MacOSVersionNumber &b)
202-
{
203-
return !(a < b);
204-
}
189+
explicit MacOSVersion(int major=-1, int minor=-1) : major(major), minor(minor) {}
205190

206-
static MacOSVersionNumber
207-
macosx_get_major_minor_version()
208-
{
209-
char tmp[1024];
210-
int major, minor, patch = 0;
211-
FILE *fp;
191+
bool operator<(const MacOSVersion &other) const {
192+
return major < other.major || (major == other.major && minor < other.minor);
193+
}
212194

213-
fp = popen("sw_vers -productVersion", "r");
214-
psmove_return_val_if_fail(fp != NULL, MacOSVersionNumber());
215-
psmove_return_val_if_fail(fgets(tmp, sizeof(tmp), fp) != NULL, MacOSVersionNumber());
216-
pclose(fp);
195+
bool operator>=(const MacOSVersion &other) const {
196+
return major > other.major || (major == other.major && minor >= other.minor);
197+
}
217198

218-
int assigned = sscanf(tmp, "%d.%d.%d", &major, &minor, &patch);
199+
bool valid() const { return major != -1 && minor != -1; }
219200

220-
/**
221-
* On Mac OS X 10.8.0, the command returns "10.8", so we allow parsing
222-
* only the first two numbers of the triplet, leaving the patch version
223-
* to the default (0) set above.
224-
*
225-
* See: https://github.com/thp/psmoveapi/issues/32
226-
**/
227-
psmove_return_val_if_fail(assigned == 2 || assigned == 3, MacOSVersionNumber());
201+
int major;
202+
int minor;
203+
};
228204

229-
return MacOSVersionNumber(major, minor);
230-
}
205+
} // end anonymous namespace
231206

232207
bool
233208
psmove_port_register_psmove(char *addr, char *host, enum PSMove_Model_Type model)
234209
{
235-
bool result = true;
236-
237210
// TODO: Host is ignored for now
238211

239212
// TODO: FIXME: If necessary, handle different controller models differently.
@@ -246,56 +219,53 @@
246219
return false;
247220
}
248221

249-
auto macos_version = macosx_get_major_minor_version();
222+
auto macos_version = MacOSVersion::running();
250223
if (!macos_version.valid()) {
251224
OSXPAIR_DEBUG("Cannot detect macOS version.\n");
252225
return false;
253-
} else if (macos_version >= MacOSVersionNumber(13, 0)) {
226+
} else if (macos_version >= MacOSVersion(13, 0)) {
254227
PSMOVE_WARNING("Pairing not yet supported on macOS Ventura, see https://github.com/thp/psmoveapi/issues/457");
255228
return false;
256-
} else if (macos_version < MacOSVersionNumber(10, 7)) {
229+
} else if (macos_version < MacOSVersion(10, 7)) {
257230
OSXPAIR_DEBUG("No need to add entry for macOS before 10.7.\n");
258231
return false;
259-
} else {
260-
OSXPAIR_DEBUG("Detected: macOS %d.%d\n", macos_version.major, macos_version.minor);
261232
}
262233

263-
std::string command = format("defaults write %s HIDDevices -array-add %s",
264-
OSX_BT_CONFIG_PATH, btaddr.c_str());
234+
OSXPAIR_DEBUG("Detected: macOS %d.%d\n", macos_version.major, macos_version.minor);
265235

266236
if (macosx_blued_is_paired(btaddr)) {
267237
OSXPAIR_DEBUG("Entry for %s already present.\n", btaddr.c_str());
268238
return true;
269239
}
270240

271-
if (macos_version < MacOSVersionNumber(10, 10))
272-
{
241+
if (macos_version < MacOSVersion(10, 10)) {
273242
if (!macosx_bluetooth_set_powered(0)) {
274243
OSXPAIR_DEBUG("Cannot shutdown Bluetooth (shut it down manually).\n");
275244
}
276245

277-
int i = 0;
278246
OSXPAIR_DEBUG("Waiting for blued shutdown (takes ca. 42s) ...\n");
279247
while (macosx_blued_running()) {
280248
psmove_port_sleep_ms(1000);
281-
i++;
282249
}
283250
OSXPAIR_DEBUG("blued successfully shutdown.\n");
284251
}
285252

253+
std::string command = format("/usr/bin/defaults write %s HIDDevices -array-add %s",
254+
OSX_BT_CONFIG_PATH, btaddr.c_str());
286255
if (geteuid() != 0) {
287256
// Not running using setuid or sudo, must use osascript to gain privileges
288-
command = format("osascript -e 'do shell script \"%s\" with administrator privileges'",
257+
command = format("/usr/bin/osascript -e 'do shell script \"%s\" with administrator privileges'",
289258
command.c_str());
290259
}
291260

292261
OSXPAIR_DEBUG("Running: '%s'\n", command.c_str());
262+
bool result = true;
293263
if (system(command.c_str()) != 0) {
294264
OSXPAIR_DEBUG("Could not run the command.");
295265
result = false;
296266
}
297267

298-
if (macos_version < MacOSVersionNumber(10, 10))
268+
if (macos_version < MacOSVersion(10, 10))
299269
{
300270
// FIXME: In OS X 10.7 this might not work - fork() and call set_powered(1)
301271
// from a fresh process (e.g. like "blueutil 1") to switch Bluetooth on
@@ -369,5 +339,23 @@
369339
char *
370340
psmove_port_get_host_bluetooth_address()
371341
{
372-
return macosx_get_btaddr();
342+
ScopedNSAutoreleasePool pool;
343+
344+
macosx_bluetooth_set_powered(1);
345+
346+
IOBluetoothHostController *controller =
347+
[IOBluetoothHostController defaultController];
348+
349+
NSString *addr = [controller addressAsString];
350+
psmove_return_val_if_fail(addr != NULL, NULL);
351+
352+
char *result = strdup([addr UTF8String]);
353+
psmove_return_val_if_fail(result != NULL, NULL);
354+
355+
if (_psmove_normalize_btaddr_inplace(result, true, ':') == NULL) {
356+
free(result);
357+
return NULL;
358+
}
359+
360+
return result;
373361
}

0 commit comments

Comments
 (0)