Browse Source

Added Ambilight-clone functionality

Thomas Buck 9 years ago
parent
commit
277c07a099

+ 1
- 1
CaseLights.ino View File

13
  * The UV command turns the UV lights on or off (s can be 0 or 1).
13
  * The UV command turns the UV lights on or off (s can be 0 or 1).
14
  */
14
  */
15
 
15
 
16
-#define DEBUG
16
+//#define DEBUG
17
 
17
 
18
 enum LoopState {
18
 enum LoopState {
19
   LOOP_IDLE,
19
   LOOP_IDLE,

+ 6
- 0
CaseLights.xcodeproj/project.pbxproj View File

10
 		E9109B111C2AACF400726111 /* SystemInfoKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9109B0B1C2AAC1B00726111 /* SystemInfoKit.framework */; };
10
 		E9109B111C2AACF400726111 /* SystemInfoKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9109B0B1C2AAC1B00726111 /* SystemInfoKit.framework */; };
11
 		E9109B151C2AAEFD00726111 /* SystemInfoKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E9109B0B1C2AAC1B00726111 /* SystemInfoKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
11
 		E9109B151C2AAEFD00726111 /* SystemInfoKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E9109B0B1C2AAC1B00726111 /* SystemInfoKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
12
 		E9109B191C2AAFE100726111 /* GPUStats.m in Sources */ = {isa = PBXBuildFile; fileRef = E9109B181C2AAFE100726111 /* GPUStats.m */; };
12
 		E9109B191C2AAFE100726111 /* GPUStats.m in Sources */ = {isa = PBXBuildFile; fileRef = E9109B181C2AAFE100726111 /* GPUStats.m */; };
13
+		E9640E8A1C30511B0081D46C /* Screenshot.m in Sources */ = {isa = PBXBuildFile; fileRef = E9640E891C30511B0081D46C /* Screenshot.m */; };
13
 		E9AB18011C29F8CD00BF3C51 /* Serial.m in Sources */ = {isa = PBXBuildFile; fileRef = E9AB18001C29F8CD00BF3C51 /* Serial.m */; };
14
 		E9AB18011C29F8CD00BF3C51 /* Serial.m in Sources */ = {isa = PBXBuildFile; fileRef = E9AB18001C29F8CD00BF3C51 /* Serial.m */; };
14
 		E9F13E031C28B3D3004C6B95 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E9F13E021C28B3D3004C6B95 /* AppDelegate.m */; };
15
 		E9F13E031C28B3D3004C6B95 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E9F13E021C28B3D3004C6B95 /* AppDelegate.m */; };
15
 		E9F13E061C28B3D3004C6B95 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E9F13E051C28B3D3004C6B95 /* main.m */; };
16
 		E9F13E061C28B3D3004C6B95 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E9F13E051C28B3D3004C6B95 /* main.m */; };
58
 		E9109B051C2AAC1B00726111 /* SystemInfoKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SystemInfoKit.xcodeproj; path = JSystemInfoKit/SystemInfoKit.xcodeproj; sourceTree = "<group>"; };
59
 		E9109B051C2AAC1B00726111 /* SystemInfoKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SystemInfoKit.xcodeproj; path = JSystemInfoKit/SystemInfoKit.xcodeproj; sourceTree = "<group>"; };
59
 		E9109B171C2AAFE100726111 /* GPUStats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GPUStats.h; path = CaseLights/GPUStats.h; sourceTree = "<group>"; };
60
 		E9109B171C2AAFE100726111 /* GPUStats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GPUStats.h; path = CaseLights/GPUStats.h; sourceTree = "<group>"; };
60
 		E9109B181C2AAFE100726111 /* GPUStats.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GPUStats.m; path = CaseLights/GPUStats.m; sourceTree = "<group>"; };
61
 		E9109B181C2AAFE100726111 /* GPUStats.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GPUStats.m; path = CaseLights/GPUStats.m; sourceTree = "<group>"; };
62
+		E9640E881C30511B0081D46C /* Screenshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Screenshot.h; path = CaseLights/Screenshot.h; sourceTree = "<group>"; };
63
+		E9640E891C30511B0081D46C /* Screenshot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Screenshot.m; path = CaseLights/Screenshot.m; sourceTree = "<group>"; };
61
 		E9AB17FF1C29F8CD00BF3C51 /* Serial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Serial.h; path = CaseLights/Serial.h; sourceTree = "<group>"; };
64
 		E9AB17FF1C29F8CD00BF3C51 /* Serial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Serial.h; path = CaseLights/Serial.h; sourceTree = "<group>"; };
62
 		E9AB18001C29F8CD00BF3C51 /* Serial.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Serial.m; path = CaseLights/Serial.m; sourceTree = "<group>"; };
65
 		E9AB18001C29F8CD00BF3C51 /* Serial.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Serial.m; path = CaseLights/Serial.m; sourceTree = "<group>"; };
63
 		E9F13DFE1C28B3D3004C6B95 /* CaseLights.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CaseLights.app; sourceTree = BUILT_PRODUCTS_DIR; };
66
 		E9F13DFE1C28B3D3004C6B95 /* CaseLights.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CaseLights.app; sourceTree = BUILT_PRODUCTS_DIR; };
95
 			children = (
98
 			children = (
96
 				E9AB17FF1C29F8CD00BF3C51 /* Serial.h */,
99
 				E9AB17FF1C29F8CD00BF3C51 /* Serial.h */,
97
 				E9AB18001C29F8CD00BF3C51 /* Serial.m */,
100
 				E9AB18001C29F8CD00BF3C51 /* Serial.m */,
101
+				E9640E881C30511B0081D46C /* Screenshot.h */,
102
+				E9640E891C30511B0081D46C /* Screenshot.m */,
98
 				E9109B171C2AAFE100726111 /* GPUStats.h */,
103
 				E9109B171C2AAFE100726111 /* GPUStats.h */,
99
 				E9109B181C2AAFE100726111 /* GPUStats.m */,
104
 				E9109B181C2AAFE100726111 /* GPUStats.m */,
100
 			);
105
 			);
255
 			files = (
260
 			files = (
256
 				E9AB18011C29F8CD00BF3C51 /* Serial.m in Sources */,
261
 				E9AB18011C29F8CD00BF3C51 /* Serial.m in Sources */,
257
 				E9109B191C2AAFE100726111 /* GPUStats.m in Sources */,
262
 				E9109B191C2AAFE100726111 /* GPUStats.m in Sources */,
263
+				E9640E8A1C30511B0081D46C /* Screenshot.m in Sources */,
258
 				E9F13E061C28B3D3004C6B95 /* main.m in Sources */,
264
 				E9F13E061C28B3D3004C6B95 /* main.m in Sources */,
259
 				E9F13E031C28B3D3004C6B95 /* AppDelegate.m in Sources */,
265
 				E9F13E031C28B3D3004C6B95 /* AppDelegate.m in Sources */,
260
 			);
266
 			);

+ 5
- 0
CaseLights/AppDelegate.h View File

18
 @property (weak) IBOutlet NSMenu *menuColors;
18
 @property (weak) IBOutlet NSMenu *menuColors;
19
 @property (weak) IBOutlet NSMenu *menuAnimations;
19
 @property (weak) IBOutlet NSMenu *menuAnimations;
20
 @property (weak) IBOutlet NSMenu *menuVisualizations;
20
 @property (weak) IBOutlet NSMenu *menuVisualizations;
21
+@property (weak) IBOutlet NSMenuItem *menuItemDisplays;
22
+@property (weak) IBOutlet NSMenu *menuDisplays;
21
 @property (weak) IBOutlet NSMenu *menuPorts;
23
 @property (weak) IBOutlet NSMenu *menuPorts;
22
 
24
 
23
 @property (weak) IBOutlet NSMenuItem *buttonOff;
25
 @property (weak) IBOutlet NSMenuItem *buttonOff;
26
 @property (weak) IBOutlet NSMenuItem *brightnessLabel;
28
 @property (weak) IBOutlet NSMenuItem *brightnessLabel;
27
 @property (weak) IBOutlet NSMenuItem *buttonLights;
29
 @property (weak) IBOutlet NSMenuItem *buttonLights;
28
 
30
 
31
+- (void)clearDisplayUI;
32
+- (void)updateDisplayUI:(NSArray *)displayIDs;
33
+
29
 @end
34
 @end
30
 
35
 

+ 121
- 3
CaseLights/AppDelegate.m View File

9
 #import "AppDelegate.h"
9
 #import "AppDelegate.h"
10
 #import "Serial.h"
10
 #import "Serial.h"
11
 #import "GPUStats.h"
11
 #import "GPUStats.h"
12
+#import "Screenshot.h"
12
 
13
 
13
 #import "SystemInfoKit/SystemInfoKit.h"
14
 #import "SystemInfoKit/SystemInfoKit.h"
14
 
15
 
61
 
62
 
62
 @synthesize statusMenu, application;
63
 @synthesize statusMenu, application;
63
 @synthesize menuColors, menuAnimations, menuVisualizations, menuPorts;
64
 @synthesize menuColors, menuAnimations, menuVisualizations, menuPorts;
65
+@synthesize menuItemDisplays, menuDisplays;
64
 @synthesize buttonOff, buttonLights;
66
 @synthesize buttonOff, buttonLights;
65
 @synthesize brightnessItem, brightnessSlider, brightnessLabel;
67
 @synthesize brightnessItem, brightnessSlider, brightnessLabel;
66
 @synthesize statusItem, statusImage;
68
 @synthesize statusItem, statusImage;
106
         for (int i = 0; i < [ports count]; i++) {
108
         for (int i = 0; i < [ports count]; i++) {
107
             // Add Menu Item for this port
109
             // Add Menu Item for this port
108
             NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
110
             NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
111
+            [item setTag:-1];
109
             [menuPorts addItem:item];
112
             [menuPorts addItem:item];
110
             
113
             
111
             // Set Enabled if it was used the last time
114
             // Set Enabled if it was used the last time
140
                     nil];
143
                     nil];
141
     for (NSString *key in [staticColors allKeys]) {
144
     for (NSString *key in [staticColors allKeys]) {
142
         NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
145
         NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
146
+        [item setTag:-1];
143
         if ([key isEqualToString:lastMode]) {
147
         if ([key isEqualToString:lastMode]) {
144
             [self selectedVisualization:item];
148
             [self selectedVisualization:item];
145
         }
149
         }
154
                                  nil];
158
                                  nil];
155
     for (NSString *key in animationStrings) {
159
     for (NSString *key in animationStrings) {
156
         NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
160
         NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
161
+        [item setTag:-1];
157
         if ([key isEqualToString:lastMode]) {
162
         if ([key isEqualToString:lastMode]) {
158
             [self selectedVisualization:item];
163
             [self selectedVisualization:item];
159
         }
164
         }
162
     
167
     
163
     // Add CPU Usage menu item
168
     // Add CPU Usage menu item
164
     NSMenuItem *cpuUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
169
     NSMenuItem *cpuUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
170
+    [cpuUsageItem setTag:-1];
165
     if ([lastMode isEqualToString:TEXT_CPU_USAGE]) {
171
     if ([lastMode isEqualToString:TEXT_CPU_USAGE]) {
166
         [self selectedVisualization:cpuUsageItem];
172
         [self selectedVisualization:cpuUsageItem];
167
     }
173
     }
169
     
175
     
170
     // Add Memory Usage item
176
     // Add Memory Usage item
171
     NSMenuItem *memoryUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_RAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
177
     NSMenuItem *memoryUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_RAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
178
+    [memoryUsageItem setTag:-1];
172
     if ([lastMode isEqualToString:TEXT_RAM_USAGE]) {
179
     if ([lastMode isEqualToString:TEXT_RAM_USAGE]) {
173
         [self selectedVisualization:memoryUsageItem];
180
         [self selectedVisualization:memoryUsageItem];
174
     }
181
     }
182
         NSLog(@"Error reading GPU information\n");
189
         NSLog(@"Error reading GPU information\n");
183
     } else {
190
     } else {
184
         NSMenuItem *itemUsage = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
191
         NSMenuItem *itemUsage = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
192
+        [itemUsage setTag:-1];
185
         if ([lastMode isEqualToString:TEXT_GPU_USAGE]) {
193
         if ([lastMode isEqualToString:TEXT_GPU_USAGE]) {
186
             [self selectedVisualization:itemUsage];
194
             [self selectedVisualization:itemUsage];
187
         }
195
         }
188
         [menuVisualizations addItem:itemUsage];
196
         [menuVisualizations addItem:itemUsage];
189
         
197
         
190
         NSMenuItem *itemVRAM = [[NSMenuItem alloc] initWithTitle:TEXT_VRAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
198
         NSMenuItem *itemVRAM = [[NSMenuItem alloc] initWithTitle:TEXT_VRAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
199
+        [itemVRAM setTag:-1];
191
         if ([lastMode isEqualToString:TEXT_VRAM_USAGE]) {
200
         if ([lastMode isEqualToString:TEXT_VRAM_USAGE]) {
192
             [self selectedVisualization:itemVRAM];
201
             [self selectedVisualization:itemVRAM];
193
         }
202
         }
206
 
215
 
207
         if ([key isEqualToString:KEY_CPU_TEMPERATURE]) {
216
         if ([key isEqualToString:KEY_CPU_TEMPERATURE]) {
208
             NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
217
             NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
218
+            [item setTag:-1];
209
             if ([lastMode isEqualToString:TEXT_CPU_TEMPERATURE]) {
219
             if ([lastMode isEqualToString:TEXT_CPU_TEMPERATURE]) {
210
                 [self selectedVisualization:item];
220
                 [self selectedVisualization:item];
211
             }
221
             }
214
         
224
         
215
         if ([key isEqualToString:KEY_GPU_TEMPERATURE]) {
225
         if ([key isEqualToString:KEY_GPU_TEMPERATURE]) {
216
             NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
226
             NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
227
+            [item setTag:-1];
217
             if ([lastMode isEqualToString:TEXT_GPU_TEMPERATURE]) {
228
             if ([lastMode isEqualToString:TEXT_GPU_TEMPERATURE]) {
218
                 [self selectedVisualization:item];
229
                 [self selectedVisualization:item];
219
             }
230
             }
235
             [serial sendString:@"UV 0\n"];
246
             [serial sendString:@"UV 0\n"];
236
         }
247
         }
237
     }
248
     }
249
+    
250
+    // List available displays and add menu items
251
+    [Screenshot init:self];
252
+    NSArray *displayIDs = [Screenshot listDisplays];
253
+    [self updateDisplayUI:displayIDs];
238
 }
254
 }
239
 
255
 
240
 - (void)applicationWillTerminate:(NSNotification *)aNotification {
256
 - (void)applicationWillTerminate:(NSNotification *)aNotification {
257
+    // Stop previous timer setting
258
+    if (animation != nil) {
259
+        [animation invalidate];
260
+        animation = nil;
261
+    }
262
+    
263
+    // Remove display callback
264
+    [Screenshot close:self];
265
+    
266
+    // Turn off all lights if possible
241
     if ([serial isOpen]) {
267
     if ([serial isOpen]) {
242
         [serial sendString:@"RGB 0 0 0\n"];
268
         [serial sendString:@"RGB 0 0 0\n"];
243
         [serial sendString:@"UV 0\n"];
269
         [serial sendString:@"UV 0\n"];
245
     }
271
     }
246
 }
272
 }
247
 
273
 
274
+- (void)clearDisplayUI {
275
+    for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
276
+        if ([[menuDisplays itemAtIndex:i] isEnabled] == YES) {
277
+            // A display configuration is currently selected. Disable the timer
278
+            if (animation != nil) {
279
+                [animation invalidate];
280
+                animation = nil;
281
+            }
282
+        }
283
+    }
284
+    [menuDisplays removeAllItems];
285
+    [menuItemDisplays setHidden:YES];
286
+}
287
+
288
+- (void)updateDisplayUI:(NSArray *)displayIDs {
289
+    if ([displayIDs count] > 0) {
290
+        NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
291
+        NSString *lastMode = [store stringForKey:PREF_LED_MODE];
292
+        [menuItemDisplays setHidden:NO];
293
+        for (int i = 0; i < [displayIDs count]; i++) {
294
+            NSString *title = [Screenshot displayNameFromDisplayID:[displayIDs objectAtIndex:i]];
295
+            NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title
296
+                                                          action:@selector(selectedVisualization:)
297
+                                                   keyEquivalent:@""];
298
+            [item setTag:[[displayIDs objectAtIndex:i] integerValue]];
299
+            if ([title isEqualToString:lastMode]) {
300
+                [self selectedVisualization:item];
301
+            }
302
+            [menuDisplays addItem:item];
303
+        }
304
+    }
305
+}
306
+
248
 - (void)setLightsR:(unsigned char)r G:(unsigned char)g B:(unsigned char)b {
307
 - (void)setLightsR:(unsigned char)r G:(unsigned char)g B:(unsigned char)b {
249
     if ([serial isOpen]) {
308
     if ([serial isOpen]) {
250
         unsigned char red = r * ([brightnessSlider floatValue] / 100.0);
309
         unsigned char red = r * ([brightnessSlider floatValue] / 100.0);
265
     for (int i = 0; i < [ports count]; i++) {
324
     for (int i = 0; i < [ports count]; i++) {
266
         // Add Menu Item for this port
325
         // Add Menu Item for this port
267
         NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
326
         NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
327
+        [item setTag:-1];
268
         [menuPorts addItem:item];
328
         [menuPorts addItem:item];
269
         
329
         
270
         // Mark it if it is currently open
330
         // Mark it if it is currently open
321
             }
381
             }
322
             [[menuVisualizations itemAtIndex:i] setState:NSOffState];
382
             [[menuVisualizations itemAtIndex:i] setState:NSOffState];
323
         }
383
         }
384
+        for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
385
+            if ([[menuDisplays itemAtIndex:i] state] == NSOnState) {
386
+                lastLEDMode = [menuDisplays itemAtIndex:i];
387
+            }
388
+            [[menuDisplays itemAtIndex:i] setState:NSOffState];
389
+        }
324
         
390
         
325
         // Turn on "off" menu item
391
         // Turn on "off" menu item
326
         [sender setState:NSOnState];
392
         [sender setState:NSOnState];
406
     return YES;
472
     return YES;
407
 }
473
 }
408
 
474
 
475
+- (void)displayVisualization:(NSMenuItem *)sender {
476
+    // Stop previous timer setting
477
+    if (animation != nil) {
478
+        [animation invalidate];
479
+        animation = nil;
480
+    }
481
+    
482
+    // Schedule next invocation for this animation...
483
+    animation = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(visualizeDisplay:) userInfo:[NSNumber numberWithInteger:[sender tag]] repeats:YES];
484
+    
485
+    // ...and also execute it right now
486
+    [animation fire];
487
+}
488
+
409
 - (void)selectedVisualization:(NSMenuItem *)sender {
489
 - (void)selectedVisualization:(NSMenuItem *)sender {
410
     // Turn off all other LED menu items
490
     // Turn off all other LED menu items
411
     if (menuColors != nil) {
491
     if (menuColors != nil) {
423
             [[menuVisualizations itemAtIndex:i] setState:NSOffState];
503
             [[menuVisualizations itemAtIndex:i] setState:NSOffState];
424
         }
504
         }
425
     }
505
     }
506
+    if (menuDisplays != nil) {
507
+        for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
508
+            [[menuDisplays itemAtIndex:i] setState:NSOffState];
509
+        }
510
+    }
426
     [buttonOff setState:NSOffState];
511
     [buttonOff setState:NSOffState];
427
     [sender setState:NSOnState];
512
     [sender setState:NSOnState];
428
     
513
     
429
-    // Check if a static color was selected
514
+    // Check if it is a display
430
     BOOL found = NO;
515
     BOOL found = NO;
431
-    if (staticColors != nil) {
516
+    if ([sender tag] > -1) {
517
+        found = YES;
518
+        [self displayVisualization:sender];
519
+    }
520
+    
521
+    // Check if a static color was selected
522
+    if ((found == NO) && (staticColors != nil)) {
432
         for (NSString *key in [staticColors allKeys]) {
523
         for (NSString *key in [staticColors allKeys]) {
433
             if ([sender.title isEqualToString:key]) {
524
             if ([sender.title isEqualToString:key]) {
434
                 found = YES;
525
                 found = YES;
450
         }
541
         }
451
     }
542
     }
452
     
543
     
453
-    if (!found) {
544
+    if (found == NO) {
454
         // Check if an animated visualization was selected
545
         // Check if an animated visualization was selected
455
         if ([self timedVisualization:[sender title]] == NO) {
546
         if ([self timedVisualization:[sender title]] == NO) {
456
             NSLog(@"Unknown LED Visualization selected!\n");
547
             NSLog(@"Unknown LED Visualization selected!\n");
529
 // ------------------- Visualizations -------------------
620
 // ------------------- Visualizations -------------------
530
 // ------------------------------------------------------
621
 // ------------------------------------------------------
531
 
622
 
623
+- (void)visualizeDisplay:(NSTimer *)timer {
624
+    NSBitmapImageRep *screen = [Screenshot screenshot:[timer userInfo]];
625
+    
626
+    if ((([screen samplesPerPixel] != 3) && ([screen samplesPerPixel] != 4)) || ([screen isPlanar] == YES) || ([screen numberOfPlanes] != 1)) {
627
+        NSLog(@"Unknown image format (%ld, %c, %ld)!\n", (long)[screen samplesPerPixel], ([screen isPlanar] == YES) ? 'p' : 'n', (long)[screen numberOfPlanes]);
628
+        return;
629
+    }
630
+    
631
+    int redC = 0, greenC = 1, blueC = 2;
632
+    if ([screen bitmapFormat] & NSAlphaFirstBitmapFormat) {
633
+        redC = 1; greenC = 2; blueC = 3;
634
+    }
635
+    
636
+    unsigned char *data = [screen bitmapData];
637
+    unsigned long long width = [screen pixelsWide];
638
+    unsigned long long height = [screen pixelsHigh];
639
+    unsigned long long max = width * height;
640
+    unsigned long long red = 0, green = 0, blue = 0;
641
+    for (unsigned long long i = 0; i < max; i++) {
642
+        red += data[([screen samplesPerPixel] * i) + redC];
643
+        green += data[([screen samplesPerPixel] * i) + greenC];
644
+        blue += data[([screen samplesPerPixel] * i) + blueC];
645
+    }
646
+    
647
+    [self setLightsR:(red / max) G:(green / max) B:(blue / max)];
648
+}
649
+
532
 - (void)visualizeGPUUsage:(NSTimer *)timer {
650
 - (void)visualizeGPUUsage:(NSTimer *)timer {
533
     NSNumber *usage;
651
     NSNumber *usage;
534
     NSNumber *freeVRAM;
652
     NSNumber *freeVRAM;

+ 6
- 0
CaseLights/Base.lproj/MainMenu.xib View File

22
                 <outlet property="buttonOff" destination="gT4-tm-Hvn" id="EEv-lf-Qo7"/>
22
                 <outlet property="buttonOff" destination="gT4-tm-Hvn" id="EEv-lf-Qo7"/>
23
                 <outlet property="menuAnimations" destination="FJm-ND-abA" id="iGI-Hg-tms"/>
23
                 <outlet property="menuAnimations" destination="FJm-ND-abA" id="iGI-Hg-tms"/>
24
                 <outlet property="menuColors" destination="ZKz-ek-t6c" id="B2S-fy-Ca6"/>
24
                 <outlet property="menuColors" destination="ZKz-ek-t6c" id="B2S-fy-Ca6"/>
25
+                <outlet property="menuDisplays" destination="Qgx-6D-9dl" id="MaT-c8-dLX"/>
26
+                <outlet property="menuItemDisplays" destination="1Cz-Xz-d16" id="rRz-n5-n2P"/>
25
                 <outlet property="menuPorts" destination="lFU-D0-M5p" id="pHd-AA-S3J"/>
27
                 <outlet property="menuPorts" destination="lFU-D0-M5p" id="pHd-AA-S3J"/>
26
                 <outlet property="menuVisualizations" destination="yZl-ae-7SI" id="b2b-dW-mvy"/>
28
                 <outlet property="menuVisualizations" destination="yZl-ae-7SI" id="b2b-dW-mvy"/>
27
                 <outlet property="statusMenu" destination="g7M-LS-DgA" id="txO-T1-8yD"/>
29
                 <outlet property="statusMenu" destination="g7M-LS-DgA" id="txO-T1-8yD"/>
691
                     <modifierMask key="keyEquivalentModifierMask"/>
693
                     <modifierMask key="keyEquivalentModifierMask"/>
692
                     <menu key="submenu" title="Visualizations" id="yZl-ae-7SI"/>
694
                     <menu key="submenu" title="Visualizations" id="yZl-ae-7SI"/>
693
                 </menuItem>
695
                 </menuItem>
696
+                <menuItem title="Displays" hidden="YES" id="1Cz-Xz-d16">
697
+                    <modifierMask key="keyEquivalentModifierMask"/>
698
+                    <menu key="submenu" title="Displays" id="Qgx-6D-9dl"/>
699
+                </menuItem>
694
                 <menuItem title="Brightness" id="Oh0-1U-yDg">
700
                 <menuItem title="Brightness" id="Oh0-1U-yDg">
695
                     <modifierMask key="keyEquivalentModifierMask"/>
701
                     <modifierMask key="keyEquivalentModifierMask"/>
696
                     <menu key="submenu" title="Brightness" id="jRu-sM-3xB">
702
                     <menu key="submenu" title="Brightness" id="jRu-sM-3xB">

+ 1
- 1
CaseLights/Info.plist View File

21
 	<key>CFBundleSignature</key>
21
 	<key>CFBundleSignature</key>
22
 	<string>????</string>
22
 	<string>????</string>
23
 	<key>CFBundleVersion</key>
23
 	<key>CFBundleVersion</key>
24
-	<string>118</string>
24
+	<string>132</string>
25
 	<key>LSApplicationCategoryType</key>
25
 	<key>LSApplicationCategoryType</key>
26
 	<string>public.app-category.utilities</string>
26
 	<string>public.app-category.utilities</string>
27
 	<key>LSMinimumSystemVersion</key>
27
 	<key>LSMinimumSystemVersion</key>

+ 23
- 0
CaseLights/Screenshot.h View File

1
+//
2
+//  Screenshot.h
3
+//  CaseLights
4
+//
5
+//  Created by Thomas Buck on 27.12.15.
6
+//  Copyright © 2015 xythobuz. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+
11
+@interface Screenshot : NSThread
12
+
13
+// Register and de-register the callback for configuration changes
14
++ (void)init:(AppDelegate *)appDelegate;
15
++ (void)close:(AppDelegate *)appDelegate;
16
+
17
+// List available displays. Returns an array of numbers.
18
++ (NSArray *)listDisplays;
19
+
20
++ (NSString *)displayNameFromDisplayID:(NSNumber *)displayID;
21
++ (NSBitmapImageRep *)screenshot:(NSNumber *)displayID;
22
+
23
+@end

+ 144
- 0
CaseLights/Screenshot.m View File

1
+//
2
+//  Screenshot.m
3
+//  CaseLights
4
+//
5
+//  Based on the Apple ScreenSnapshot example:
6
+//  https://developer.apple.com/library/mac/samplecode/ScreenSnapshot/Listings/ScreenSnapshot_ScreenSnapshotAppDelegate_m.html
7
+//
8
+//  Created by Thomas Buck on 27.12.15.
9
+//  Copyright © 2015 xythobuz. All rights reserved.
10
+//
11
+
12
+// Uncomment to store a screenshot for each display in the build directory
13
+//#define DEBUG_SCREENSHOT
14
+
15
+#import "AppDelegate.h"
16
+#import "Screenshot.h"
17
+
18
+static BOOL displayRegistrationCallBackSuccessful;
19
+
20
+static void displayRegisterReconfigurationCallback(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *userInfo) {
21
+    AppDelegate *appDelegate = (__bridge AppDelegate*)userInfo;
22
+    static BOOL DisplayConfigurationChanged = NO;
23
+    
24
+    // Before display reconfiguration, this callback fires to inform
25
+    // applications of a pending configuration change. The callback runs
26
+    // once for each on-line display.  The flags passed in are set to
27
+    // kCGDisplayBeginConfigurationFlag.  This callback does not
28
+    // carry other per-display information, as details of how a
29
+    // reconfiguration affects a particular device rely on device-specific
30
+    // behaviors which may not be exposed by a device driver.
31
+    //
32
+    // After display reconfiguration, at the time the callback function
33
+    // is invoked, all display state reported by CoreGraphics, QuickDraw,
34
+    // and the Carbon Display Manager API will be up to date.  This callback
35
+    // runs after the Carbon Display Manager notification callbacks.
36
+    // The callback runs once for each added, removed, and currently
37
+    // on-line display.  Note that in the case of removed displays, calls into
38
+    // the CoreGraphics API with the removed display ID will fail.
39
+    
40
+    // Because the callback is called for each display I use DisplayConfigurationChanged to
41
+    // make sure we only disable the menu to change displays once and then refresh it only once.
42
+    if (flags == kCGDisplayBeginConfigurationFlag) {
43
+        if (DisplayConfigurationChanged == NO) {
44
+            [appDelegate clearDisplayUI];
45
+            DisplayConfigurationChanged = YES;
46
+        }
47
+    } else if (DisplayConfigurationChanged == YES) {
48
+        [appDelegate updateDisplayUI:[Screenshot listDisplays]];
49
+        DisplayConfigurationChanged = NO;
50
+    }
51
+}
52
+
53
+@implementation Screenshot
54
+
55
++ (void)init:(AppDelegate *)appDelegate {
56
+    // Applications who want to register for notifications of display changes would use
57
+    // CGDisplayRegisterReconfigurationCallback
58
+    //
59
+    // Display changes are reported via a callback mechanism.
60
+    //
61
+    // Callbacks are invoked when the app is listening for events,
62
+    // on the event processing thread, or from within the display
63
+    // reconfiguration function when in the program that is driving the
64
+    // reconfiguration.
65
+    displayRegistrationCallBackSuccessful = NO; // Hasn't been tried yet.
66
+    CGError err = CGDisplayRegisterReconfigurationCallback(displayRegisterReconfigurationCallback, (__bridge void * _Nullable)(appDelegate));
67
+    if (err == kCGErrorSuccess) {
68
+        displayRegistrationCallBackSuccessful = YES;
69
+    }
70
+}
71
+
72
++ (void)close:(AppDelegate *)appDelegate {
73
+    // CGDisplayRemoveReconfigurationCallback Removes the registration of a callback function that’s invoked
74
+    // whenever a local display is reconfigured.  We only remove the registration if it was successful in the first place.
75
+    if (displayRegistrationCallBackSuccessful == YES) {
76
+        CGDisplayRemoveReconfigurationCallback(displayRegisterReconfigurationCallback, (__bridge void * _Nullable)(appDelegate));
77
+    }
78
+}
79
+
80
++ (NSArray *)listDisplays {
81
+    CGDisplayCount dspCount = 0;
82
+    CGError err = CGGetActiveDisplayList(0, NULL, &dspCount);
83
+    if (err != CGDisplayNoErr) {
84
+        NSLog(@"Couldn't list any active displays (%d)!\n", err);
85
+        return nil;
86
+    }
87
+    
88
+    CGDirectDisplayID *displays = calloc((size_t)dspCount, sizeof(CGDirectDisplayID));
89
+    err = CGGetActiveDisplayList(dspCount, displays, &dspCount);
90
+    if (err != CGDisplayNoErr) {
91
+        NSLog(@"Couldn't get active display list (%d)!\n", err);
92
+        return nil;
93
+    }
94
+    
95
+    if (dspCount > 0) {
96
+        NSMutableArray *array = [NSMutableArray arrayWithCapacity:dspCount];
97
+        for (int i = 0; i < dspCount; i++) {
98
+            [array addObject:[NSNumber numberWithInt:displays[i]]];
99
+        }
100
+    
101
+        return [array copy];
102
+    } else {
103
+        NSLog(@"No displays found!\n");
104
+        return nil;
105
+    }
106
+}
107
+
108
++ (NSString *)displayNameFromDisplayID:(NSNumber *)displayID {
109
+    NSDictionary *displayInfo = CFBridgingRelease(IODisplayCreateInfoDictionary(CGDisplayIOServicePort([displayID unsignedIntValue]), kIODisplayOnlyPreferredName));
110
+    NSDictionary *localizedNames = [displayInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
111
+    
112
+    // Use the first name
113
+    NSString *displayProductName = nil;
114
+    if ([localizedNames count] > 0) {
115
+        displayProductName = [localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]];
116
+        
117
+#ifdef DEBUG
118
+        NSLog(@"Display %u named \"%@\"!\n", [displayID unsignedIntValue], displayProductName);
119
+#endif
120
+    }
121
+    
122
+    return displayProductName;
123
+}
124
+
125
++ (NSBitmapImageRep *)screenshot:(NSNumber *)displayID {
126
+    CGImageRef image = CGDisplayCreateImage([displayID unsignedIntValue]);
127
+    NSBitmapImageRep *imgRep = [[NSBitmapImageRep alloc] initWithCGImage:image];
128
+    CFRelease(image);
129
+    
130
+#ifdef DEBUG_SCREENSHOT
131
+    NSData *data = [imgRep representationUsingType:NSPNGFileType properties:[NSDictionary dictionary]];
132
+    NSString *path = [NSString stringWithFormat:@"test-%u-%@.png", [displayID unsignedIntValue], [Screenshot displayNameFromDisplayID:displayID]];
133
+    NSError *error;
134
+    if ([data writeToFile:path options:0 error:&error] == YES) {
135
+        NSLog(@"Wrote debug image to \"%@\"\n", path);
136
+    } else {
137
+        NSLog(@"Error writing debug image to \"%@\": %@\n", path, error);
138
+    }
139
+#endif
140
+    
141
+    return imgRep;
142
+}
143
+
144
+@end

+ 10
- 0
CaseLights/Serial.m View File

21
 
21
 
22
 #import "Serial.h"
22
 #import "Serial.h"
23
 
23
 
24
+#define MAX_SEND_ERRORS 10
25
+
24
 @interface Serial ()
26
 @interface Serial ()
25
 
27
 
26
 @property (assign) int fd;
28
 @property (assign) int fd;
157
     NSLog(@"Sending string %s", data);
159
     NSLog(@"Sending string %s", data);
158
 #endif
160
 #endif
159
     
161
     
162
+    int errorCount = 0;
160
     ssize_t sent = 0;
163
     ssize_t sent = 0;
161
     while (sent < length) {
164
     while (sent < length) {
162
         ssize_t ret = write(fd, data + sent, length - sent);
165
         ssize_t ret = write(fd, data + sent, length - sent);
163
         if (ret < 0) {
166
         if (ret < 0) {
164
             NSLog(@"Error writing to serial port: %s (%d)!\n", strerror(errno), errno);
167
             NSLog(@"Error writing to serial port: %s (%d)!\n", strerror(errno), errno);
168
+            errorCount++;
169
+            if (errorCount >= MAX_SEND_ERRORS) {
170
+#ifdef DEBUG
171
+                NSLog(@"Too many send errors! Giving up...\n");
172
+#endif
173
+                return;
174
+            }
165
         } else {
175
         } else {
166
             sent += ret;
176
             sent += ret;
167
         }
177
         }

Loading…
Cancel
Save