cocoadock 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/cocoadock/cocoadock.m +296 -51
- data/lib/cocoadock/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 854f6ae805d2626929da28f513cd471e2c252be893a0c062823f52d157b0bb44
|
|
4
|
+
data.tar.gz: 632c33c8faaa92152b5a68a8d9affe598fe8afcbf0b3bc5691f19def81b94ae3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b3688a9af53f4d84559ac1374485dce4a38bf020a86427a0d7f2d5ce19b9eafcca844f7d630a568e42204b3401a4c44e592f95b2b57d19e1581af1474297ebe6
|
|
7
|
+
data.tar.gz: 6547eff88bb43c512e2879439c9891d5dcfed40ae8afe901c0582d6145fa2d6f1974f66360b6d29b41a166892690985e1b8b826c07e041c8c84ad8d040b17c75
|
data/ext/cocoadock/cocoadock.m
CHANGED
|
@@ -5,71 +5,302 @@ VALUE rb_mCocoadock;
|
|
|
5
5
|
VALUE rb_mCocoadockClass;
|
|
6
6
|
|
|
7
7
|
VALUE cocoadock_initialize(VALUE self);
|
|
8
|
+
VALUE cocoadock_is_app_to_dock(VALUE self, VALUE path);
|
|
8
9
|
VALUE cocoadock_add_app_to_dock(VALUE self, VALUE path);
|
|
9
10
|
VALUE cocoadock_remove_app_from_dock(VALUE self, VALUE path);
|
|
11
|
+
VALUE cocoadock_remove_others_in_dock(VALUE self);
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
NSMutableArray *LoadDockOthers(void) {
|
|
14
|
+
CFArrayRef persistentOthers = CFPreferencesCopyAppValue(
|
|
15
|
+
CFSTR("persistent-others"),
|
|
16
|
+
CFSTR("com.apple.dock")
|
|
17
|
+
);
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
if (!persistentOthers) {
|
|
20
|
+
return [NSMutableArray array];
|
|
21
|
+
}
|
|
17
22
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@"'<dict>"
|
|
21
|
-
"<key>tile-data</key><dict>"
|
|
22
|
-
"<key>file-data</key><dict>"
|
|
23
|
-
"<key>_CFURLString</key><string>%@</string>"
|
|
24
|
-
"<key>_CFURLStringType</key><integer>15</integer>"
|
|
25
|
-
"</dict>"
|
|
26
|
-
"</dict>"
|
|
27
|
-
"</dict>'", urlString];
|
|
23
|
+
NSMutableArray *others =
|
|
24
|
+
[(__bridge NSArray *)persistentOthers mutableCopy];
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
CFRelease(persistentOthers);
|
|
27
|
+
return others;
|
|
28
|
+
}
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
void SaveDockOthers(NSArray *others) {
|
|
31
|
+
CFPreferencesSetAppValue(
|
|
32
|
+
CFSTR("persistent-others"),
|
|
33
|
+
(__bridge CFArrayRef)others,
|
|
34
|
+
CFSTR("com.apple.dock")
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
CFPreferencesAppSynchronize(CFSTR("com.apple.dock"));
|
|
38
|
+
|
|
39
|
+
system("killall Dock");
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
42
|
+
BOOL RemoveOthersInDock(void) {
|
|
43
|
+
|
|
44
|
+
int attempts = 0;
|
|
45
|
+
|
|
46
|
+
while (attempts < 3) {
|
|
47
|
+
|
|
48
|
+
NSMutableArray *others = LoadDockOthers();
|
|
49
|
+
|
|
50
|
+
if (others.count == 0) {
|
|
51
|
+
return YES; // Already empty
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
[others removeAllObjects];
|
|
55
|
+
|
|
56
|
+
SaveDockOthers(others);
|
|
57
|
+
|
|
58
|
+
// Give Dock time to restart and commit
|
|
59
|
+
[NSThread sleepForTimeInterval:0.25];
|
|
60
|
+
|
|
61
|
+
// Verify
|
|
62
|
+
NSMutableArray *verify = LoadDockOthers();
|
|
63
|
+
if (verify.count == 0) {
|
|
64
|
+
return YES;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
attempts++;
|
|
68
|
+
[NSThread sleepForTimeInterval:0.1];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return NO;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
BOOL IsAppInDock(NSString *bundleID) {
|
|
76
|
+
if (!bundleID) return NO;
|
|
77
|
+
|
|
78
|
+
// Load Dock plist directly
|
|
79
|
+
NSString *plistPath = [@"~/Library/Preferences/com.apple.dock.plist" stringByExpandingTildeInPath];
|
|
80
|
+
NSDictionary *dockPlist = [NSDictionary dictionaryWithContentsOfFile:plistPath];
|
|
81
|
+
NSArray *persistentApps = dockPlist[@"persistent-apps"];
|
|
82
|
+
if (!persistentApps) return NO;
|
|
83
|
+
|
|
84
|
+
for (NSDictionary *item in persistentApps) {
|
|
85
|
+
NSDictionary *tileData = item[@"tile-data"];
|
|
86
|
+
if (!tileData) continue;
|
|
87
|
+
|
|
88
|
+
// 1️⃣ Check bundle identifier
|
|
89
|
+
NSString *appBundleID = tileData[@"bundle-identifier"];
|
|
90
|
+
if ([appBundleID isEqualToString:bundleID]) {
|
|
91
|
+
return YES;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 2️⃣ Fallback: check file path
|
|
57
95
|
NSDictionary *fileData = tileData[@"file-data"];
|
|
58
|
-
NSString *
|
|
59
|
-
if (!
|
|
60
|
-
|
|
96
|
+
NSString *path = fileData[@"_CFURLString"];
|
|
97
|
+
if (!path) continue;
|
|
98
|
+
|
|
99
|
+
// Handle possible URL or relative path
|
|
100
|
+
if (![path hasPrefix:@"/"]) {
|
|
101
|
+
NSURL *url = [NSURL URLWithString:path];
|
|
102
|
+
path = url.path;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!path) continue;
|
|
106
|
+
|
|
107
|
+
// Resolve symlinks
|
|
108
|
+
path = [[NSURL fileURLWithPath:path] URLByResolvingSymlinksInPath].path;
|
|
109
|
+
|
|
110
|
+
NSBundle *bundle = [NSBundle bundleWithPath:path];
|
|
111
|
+
if (bundle && [bundle.bundleIdentifier isEqualToString:bundleID]) {
|
|
112
|
+
return YES;
|
|
61
113
|
}
|
|
62
114
|
}
|
|
63
115
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
[dockPlist writeToFile:@"/tmp/com.apple.dock.plist" atomically:YES];
|
|
116
|
+
return NO;
|
|
117
|
+
}
|
|
67
118
|
|
|
68
|
-
NSString *importCmd = @"defaults import com.apple.dock /tmp/com.apple.dock.plist";
|
|
69
|
-
system([importCmd UTF8String]);
|
|
70
119
|
|
|
71
|
-
|
|
72
|
-
|
|
120
|
+
NSURL *AppURLForBundleID(NSString *bundleID) {
|
|
121
|
+
if (!bundleID) return nil;
|
|
122
|
+
|
|
123
|
+
CFArrayRef urls = LSCopyApplicationURLsForBundleIdentifier(
|
|
124
|
+
(__bridge CFStringRef)bundleID,
|
|
125
|
+
NULL
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (!urls || CFArrayGetCount(urls) == 0) {
|
|
129
|
+
if (urls) CFRelease(urls);
|
|
130
|
+
return nil;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
CFURLRef firstURL = CFArrayGetValueAtIndex(urls, 0);
|
|
134
|
+
NSURL *appURL = (__bridge NSURL *)firstURL;
|
|
135
|
+
|
|
136
|
+
CFRelease(urls); // release the array, not the item
|
|
137
|
+
return appURL;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
NSDictionary *DockTileForAppURL(NSURL *appURL) {
|
|
141
|
+
if (!appURL) return nil;
|
|
142
|
+
|
|
143
|
+
NSDictionary *fileData = @{
|
|
144
|
+
@"_CFURLString": appURL.absoluteString,
|
|
145
|
+
@"_CFURLStringType": @15
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
NSDictionary *tileData = @{
|
|
149
|
+
@"file-data": fileData
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return @{
|
|
153
|
+
@"tile-type": @"file-tile",
|
|
154
|
+
@"tile-data": tileData
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
void AddDockAppByBundleID(NSString *bundleID) {
|
|
159
|
+
CFArrayRef persistentApps = CFPreferencesCopyAppValue(
|
|
160
|
+
CFSTR("persistent-apps"),
|
|
161
|
+
CFSTR("com.apple.dock")
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
if (!persistentApps) {
|
|
165
|
+
NSLog(@"Failed to read Dock preferences");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
NSMutableArray *apps =
|
|
170
|
+
[(__bridge NSArray *)persistentApps mutableCopy];
|
|
171
|
+
|
|
172
|
+
if (!apps || !bundleID) return;
|
|
173
|
+
|
|
174
|
+
NSURL *appURL = AppURLForBundleID(bundleID);
|
|
175
|
+
if (!appURL) {
|
|
176
|
+
NSLog(@"Could not resolve app for bundle ID %@", bundleID);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
NSString *targetPath = appURL.path;
|
|
181
|
+
|
|
182
|
+
// Prevent duplicates
|
|
183
|
+
for (NSDictionary *item in apps) {
|
|
184
|
+
NSDictionary *fileData = item[@"tile-data"][@"file-data"];
|
|
185
|
+
NSString *urlString = fileData[@"_CFURLString"];
|
|
186
|
+
if (!urlString) continue;
|
|
187
|
+
|
|
188
|
+
NSString *path = [[NSURL URLWithString:urlString] path];
|
|
189
|
+
if ([path isEqualToString:targetPath]) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
NSDictionary *dockItem = DockTileForAppURL(appURL);
|
|
195
|
+
if (dockItem) {
|
|
196
|
+
[apps addObject:dockItem];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
CFPreferencesSetAppValue(
|
|
200
|
+
CFSTR("persistent-apps"),
|
|
201
|
+
(__bridge CFArrayRef)apps,
|
|
202
|
+
CFSTR("com.apple.dock")
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
CFPreferencesAppSynchronize(CFSTR("com.apple.dock"));
|
|
206
|
+
|
|
207
|
+
CFRelease(persistentApps);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
void RemoveDockAppByBundleID(NSString *bundleID) {
|
|
211
|
+
CFArrayRef persistentApps = CFPreferencesCopyAppValue(
|
|
212
|
+
CFSTR("persistent-apps"),
|
|
213
|
+
CFSTR("com.apple.dock")
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
NSMutableArray *apps =
|
|
217
|
+
[(__bridge NSArray *)persistentApps mutableCopy];
|
|
218
|
+
|
|
219
|
+
if (!apps || !bundleID) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
for (NSInteger i = apps.count - 1; i >= 0; i--) {
|
|
224
|
+
NSDictionary *item = apps[i];
|
|
225
|
+
NSDictionary *tileData = item[@"tile-data"];
|
|
226
|
+
|
|
227
|
+
NSString *dockBundleID = tileData[@"bundle-identifier"];
|
|
228
|
+
if ([dockBundleID isEqualToString:bundleID]) {
|
|
229
|
+
[apps removeObjectAtIndex:i];
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
NSDictionary *fileData = tileData[@"file-data"];
|
|
234
|
+
NSString *urlString = fileData[@"_CFURLString"];
|
|
235
|
+
if (urlString) {
|
|
236
|
+
NSString *path = [[NSURL URLWithString:urlString] path];
|
|
237
|
+
if ([path containsString:bundleID]) {
|
|
238
|
+
[apps removeObjectAtIndex:i];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
CFPreferencesSetAppValue(
|
|
244
|
+
CFSTR("persistent-apps"),
|
|
245
|
+
(__bridge CFArrayRef)apps,
|
|
246
|
+
CFSTR("com.apple.dock")
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
CFPreferencesAppSynchronize(CFSTR("com.apple.dock"));
|
|
250
|
+
|
|
251
|
+
if (persistentApps) {
|
|
252
|
+
CFRelease(persistentApps);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
BOOL isAppInDock(const char *app_path) {
|
|
257
|
+
NSString *appPath = [[NSString alloc] initWithCString:app_path encoding:NSUTF8StringEncoding];
|
|
258
|
+
NSBundle *bundle = [NSBundle bundleWithPath:appPath];
|
|
259
|
+
|
|
260
|
+
if (!bundle) {
|
|
261
|
+
NSLog(@"Not a valid app bundle");
|
|
262
|
+
return NO;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
NSString *bundleID = bundle.bundleIdentifier;
|
|
266
|
+
|
|
267
|
+
return IsAppInDock(bundleID);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
void addAppToDock(const char *app_path) {
|
|
271
|
+
@autoreleasepool {
|
|
272
|
+
NSString *appPath = [[NSString alloc] initWithCString:app_path encoding:NSUTF8StringEncoding];
|
|
273
|
+
|
|
274
|
+
NSBundle *bundle = [NSBundle bundleWithPath:appPath];
|
|
275
|
+
|
|
276
|
+
if (!bundle) {
|
|
277
|
+
NSLog(@"Not a valid app bundle");
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
NSString *bundleID = bundle.bundleIdentifier;
|
|
282
|
+
AddDockAppByBundleID(bundleID);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
void removeAppFromDock(const char* app_path) {
|
|
287
|
+
@autoreleasepool {
|
|
288
|
+
NSString *appPath = [[NSString alloc] initWithCString:app_path encoding:NSUTF8StringEncoding];
|
|
289
|
+
|
|
290
|
+
NSBundle *bundle = [NSBundle bundleWithPath:appPath];
|
|
291
|
+
|
|
292
|
+
if (!bundle) {
|
|
293
|
+
NSLog(@"Not a valid app bundle");
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
NSString *bundleID = bundle.bundleIdentifier;
|
|
298
|
+
RemoveDockAppByBundleID(bundleID);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
void removeOthersInDock () {
|
|
303
|
+
RemoveOthersInDock();
|
|
73
304
|
}
|
|
74
305
|
|
|
75
306
|
RUBY_FUNC_EXPORTED void
|
|
@@ -79,7 +310,9 @@ Init_cocoadock(void)
|
|
|
79
310
|
rb_mCocoadockClass = rb_define_class_under(rb_mCocoadock, "CocoaDock", rb_cObject);
|
|
80
311
|
rb_define_method(rb_mCocoadockClass, "initialize", cocoadock_initialize, 0);
|
|
81
312
|
rb_define_method(rb_mCocoadockClass, "add_app", cocoadock_add_app_to_dock, 1);
|
|
313
|
+
rb_define_method(rb_mCocoadockClass, "app_in_dock?", cocoadock_is_app_to_dock, 1);
|
|
82
314
|
rb_define_method(rb_mCocoadockClass, "remove_app", cocoadock_remove_app_from_dock, 1);
|
|
315
|
+
rb_define_method(rb_mCocoadockClass, "remove_others", cocoadock_remove_others_in_dock, 0);
|
|
83
316
|
}
|
|
84
317
|
|
|
85
318
|
VALUE cocoadock_initialize(VALUE self) {
|
|
@@ -87,9 +320,17 @@ VALUE cocoadock_initialize(VALUE self) {
|
|
|
87
320
|
return self;
|
|
88
321
|
}
|
|
89
322
|
|
|
323
|
+
VALUE cocoadock_is_app_to_dock(VALUE self, VALUE path) {
|
|
324
|
+
const char *c_app_path = StringValueCStr(path);
|
|
325
|
+
BOOL result = isAppInDock(c_app_path);
|
|
326
|
+
if (result == YES) {
|
|
327
|
+
return Qtrue;
|
|
328
|
+
}
|
|
329
|
+
return Qfalse;
|
|
330
|
+
}
|
|
331
|
+
|
|
90
332
|
VALUE cocoadock_add_app_to_dock(VALUE self, VALUE path) {
|
|
91
333
|
const char *c_app_path = StringValueCStr(path);
|
|
92
|
-
//NSString *appPath = [[NSString alloc] initWithCString:c_app_path encoding:NSUTF8StringEncoding];
|
|
93
334
|
addAppToDock(c_app_path);
|
|
94
335
|
}
|
|
95
336
|
|
|
@@ -98,3 +339,7 @@ VALUE cocoadock_remove_app_from_dock(VALUE self, VALUE path) {
|
|
|
98
339
|
//NSString *appPath = [[NSString alloc] initWithCString:c_app_path encoding:NSUTF8StringEncoding];
|
|
99
340
|
removeAppFromDock(c_app_path);
|
|
100
341
|
}
|
|
342
|
+
|
|
343
|
+
VALUE cocoadock_remove_others_in_dock(VALUE self) {
|
|
344
|
+
removeOthersInDock();
|
|
345
|
+
}
|
data/lib/cocoadock/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cocoadock
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tommy Jeff
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-02-11 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: Cocoa dock functions for Ruby on macOS
|
|
13
13
|
email:
|