Simple RGB LED controller for Mac OS X
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AppDelegate.m 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. //
  2. // AppDelegate.m
  3. // CaseLights
  4. //
  5. // Created by Thomas Buck on 21.12.15.
  6. // Copyright © 2015 xythobuz. All rights reserved.
  7. //
  8. #import "AppDelegate.h"
  9. #import "Serial.h"
  10. #import "GPUStats.h"
  11. #import <SystemInfoKit/SystemInfoKit.h>
  12. #define PREF_SERIAL_PORT @"SerialPort"
  13. #define PREF_LIGHTS_STATE @"LightState"
  14. #define PREF_LED_MODE @"LEDMode"
  15. #define TEXT_CPU_USAGE @"CPU Usage"
  16. #define TEXT_RAM_USAGE @"RAM Usage"
  17. #define TEXT_GPU_USAGE @"GPU Usage"
  18. #define TEXT_VRAM_USAGE @"VRAM Usage"
  19. #define TEXT_CPU_TEMPERATURE @"CPU Temperature"
  20. #define TEXT_GPU_TEMPERATURE @"GPU Temperature"
  21. #define TEXT_RGB_FADE @"RGB Fade"
  22. #define TEXT_HSV_FADE @"HSV Fade"
  23. #define TEXT_RANDOM @"Random"
  24. #define KEY_CPU_TEMPERATURE @"TC0D"
  25. #define KEY_GPU_TEMPERATURE @"TG0D"
  26. // Temperature in Celsius
  27. #define CPU_TEMP_MIN 20
  28. #define CPU_TEMP_MAX 90
  29. // HSV Color (S = V = 1)
  30. #define CPU_COLOR_MIN 120
  31. #define CPU_COLOR_MAX 0
  32. #define GPU_TEMP_MIN 20
  33. #define GPU_TEMP_MAX 90
  34. #define GPU_COLOR_MIN 120
  35. #define GPU_COLOR_MAX 0
  36. #define RAM_COLOR_MIN 0
  37. #define RAM_COLOR_MAX 120
  38. @interface AppDelegate ()
  39. @property (strong) NSStatusItem *statusItem;
  40. @property (strong) NSImage *statusImage;
  41. @property (strong) NSDictionary *staticColors;
  42. @property (strong) NSTimer *animation;
  43. @property (strong) Serial *serial;
  44. @property (strong) NSMenuItem *lastLEDMode;
  45. @end
  46. @implementation AppDelegate
  47. @synthesize statusMenu, application;
  48. @synthesize menuColors, menuAnimations, menuVisualizations, menuPorts;
  49. @synthesize buttonOff, buttonLights;
  50. @synthesize statusItem, statusImage;
  51. @synthesize staticColors, animation;
  52. @synthesize serial, lastLEDMode;
  53. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
  54. srand((unsigned)time(NULL));
  55. serial = [[Serial alloc] init];
  56. lastLEDMode = nil;
  57. animation = nil;
  58. // Prepare status bar menu
  59. statusImage = [NSImage imageNamed:@"MenuIcon"];
  60. [statusImage setTemplate:YES];
  61. statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
  62. [statusItem setImage:statusImage];
  63. [statusItem setMenu:statusMenu];
  64. // Set default configuration values, load existing ones
  65. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  66. NSMutableDictionary *appDefaults = [NSMutableDictionary dictionaryWithObject:@"" forKey:PREF_SERIAL_PORT];
  67. [appDefaults setObject:[NSNumber numberWithBool:NO] forKey:PREF_LIGHTS_STATE];
  68. [appDefaults setObject:@"" forKey:PREF_LED_MODE];
  69. [store registerDefaults:appDefaults];
  70. [store synchronize];
  71. NSString *savedPort = [store stringForKey:PREF_SERIAL_PORT];
  72. BOOL turnOnLights = [store boolForKey:PREF_LIGHTS_STATE];
  73. NSString *lastMode = [store stringForKey:PREF_LED_MODE];
  74. // Prepare serial port menu
  75. NSArray *ports = [Serial listSerialPorts];
  76. if ([ports count] > 0) {
  77. [menuPorts removeAllItems];
  78. for (int i = 0; i < [ports count]; i++) {
  79. // Add Menu Item for this port
  80. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
  81. [menuPorts addItem:item];
  82. // Set Enabled if it was used the last time
  83. if ((savedPort != nil) && [[ports objectAtIndex:i] isEqualToString:savedPort]) {
  84. [[menuPorts itemAtIndex:i] setState:NSOnState];
  85. // Try to open serial port
  86. [serial setPortName:savedPort];
  87. if ([serial openPort]) {
  88. // Unselect it when an error occured opening the port
  89. [[menuPorts itemAtIndex:i] setState:NSOffState];
  90. }
  91. }
  92. }
  93. }
  94. // Select "Off" button if it was last selected
  95. if ([lastMode isEqualToString:@""]) {
  96. [buttonOff setState:NSOffState];
  97. [self turnLEDsOff:buttonOff];
  98. }
  99. // Prepare static colors menu
  100. staticColors = [NSDictionary dictionaryWithObjectsAndKeys:
  101. [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:0.0f alpha:0.0f], @"Red",
  102. [NSColor colorWithCalibratedRed:0.0f green:1.0f blue:0.0f alpha:0.0f], @"Green",
  103. [NSColor colorWithCalibratedRed:0.0f green:0.0f blue:1.0f alpha:0.0f], @"Blue",
  104. [NSColor colorWithCalibratedRed:0.0f green:1.0f blue:1.0f alpha:0.0f], @"Cyan",
  105. [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:1.0f alpha:0.0f], @"Magenta",
  106. [NSColor colorWithCalibratedRed:1.0f green:1.0f blue:0.0f alpha:0.0f], @"Yellow",
  107. [NSColor colorWithCalibratedRed:1.0f green:1.0f blue:1.0f alpha:0.0f], @"White",
  108. nil];
  109. for (NSString *key in [staticColors allKeys]) {
  110. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
  111. if ([key isEqualToString:lastMode]) {
  112. [self selectedVisualization:item];
  113. }
  114. [menuColors addItem:item];
  115. }
  116. // Prepare animations menu
  117. NSArray *animationStrings = [NSArray arrayWithObjects:
  118. TEXT_RGB_FADE,
  119. TEXT_HSV_FADE,
  120. TEXT_RANDOM,
  121. nil];
  122. for (NSString *key in animationStrings) {
  123. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
  124. if ([key isEqualToString:lastMode]) {
  125. [self selectedVisualization:item];
  126. }
  127. [menuAnimations addItem:item];
  128. }
  129. // Add CPU Usage menu item
  130. NSMenuItem *cpuUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  131. if ([lastMode isEqualToString:TEXT_CPU_USAGE]) {
  132. [self selectedVisualization:cpuUsageItem];
  133. }
  134. [menuVisualizations addItem:cpuUsageItem];
  135. // Add Memory Usage item
  136. NSMenuItem *memoryUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_RAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  137. if ([lastMode isEqualToString:TEXT_RAM_USAGE]) {
  138. [self selectedVisualization:memoryUsageItem];
  139. }
  140. [menuVisualizations addItem:memoryUsageItem];
  141. // Check if GPU Stats are available, add menu items if so
  142. NSNumber *usage;
  143. NSNumber *freeVRAM;
  144. NSNumber *usedVRAM;
  145. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  146. NSLog(@"Error reading GPU information\n");
  147. } else {
  148. NSMenuItem *itemUsage = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  149. if ([lastMode isEqualToString:TEXT_GPU_USAGE]) {
  150. [self selectedVisualization:itemUsage];
  151. }
  152. [menuVisualizations addItem:itemUsage];
  153. NSMenuItem *itemVRAM = [[NSMenuItem alloc] initWithTitle:TEXT_VRAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  154. if ([lastMode isEqualToString:TEXT_VRAM_USAGE]) {
  155. [self selectedVisualization:itemVRAM];
  156. }
  157. [menuVisualizations addItem:itemVRAM];
  158. }
  159. // Check available temperatures and add menu items
  160. JSKSMC *smc = [JSKSMC smc];
  161. for (int i = 0; i < [[smc workingTempKeys] count]; i++) {
  162. NSString *key = [smc.workingTempKeys objectAtIndex:i];
  163. #ifdef DEBUG
  164. NSString *name = [smc humanReadableNameForKey:key];
  165. NSLog(@"Sensor \"%@\": \"%@\"\n", key, name);
  166. #endif
  167. if ([key isEqualToString:KEY_CPU_TEMPERATURE]) {
  168. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
  169. if ([lastMode isEqualToString:TEXT_CPU_TEMPERATURE]) {
  170. [self selectedVisualization:item];
  171. }
  172. [menuVisualizations addItem:item];
  173. }
  174. if ([key isEqualToString:KEY_GPU_TEMPERATURE]) {
  175. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
  176. if ([lastMode isEqualToString:TEXT_GPU_TEMPERATURE]) {
  177. [self selectedVisualization:item];
  178. }
  179. [menuVisualizations addItem:item];
  180. }
  181. }
  182. // Restore previously used lights configuration
  183. if (turnOnLights) {
  184. // Turn on lights
  185. if ([serial isOpen]) {
  186. [serial sendString:@"UV 1\n"];
  187. }
  188. [buttonLights setState:NSOnState];
  189. } else {
  190. // Turn off lights
  191. if ([serial isOpen]) {
  192. [serial sendString:@"UV 0\n"];
  193. }
  194. }
  195. }
  196. - (void)applicationWillTerminate:(NSNotification *)aNotification {
  197. // Close serial port, if it was opened
  198. if ([serial isOpen]) {
  199. [serial closePort];
  200. }
  201. }
  202. - (IBAction)relistSerialPorts:(id)sender {
  203. // Refill port list
  204. NSArray *ports = [Serial listSerialPorts];
  205. [menuPorts removeAllItems];
  206. for (int i = 0; i < [ports count]; i++) {
  207. // Add Menu Item for this port
  208. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
  209. [menuPorts addItem:item];
  210. // Mark it if it is currently open
  211. if ([serial isOpen]) {
  212. if ([[ports objectAtIndex:i] isEqualToString:[serial portName]]) {
  213. [[menuPorts itemAtIndex:i] setState:NSOnState];
  214. }
  215. }
  216. }
  217. }
  218. - (IBAction)turnLEDsOff:(NSMenuItem *)sender {
  219. if ([sender state] == NSOffState) {
  220. lastLEDMode = nil;
  221. // Stop previous timer setting
  222. if (animation != nil) {
  223. [animation invalidate];
  224. animation = nil;
  225. }
  226. // Turn off all other LED menu items
  227. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  228. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  229. lastLEDMode = [menuColors itemAtIndex:i];
  230. }
  231. [[menuColors itemAtIndex:i] setState:NSOffState];
  232. }
  233. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  234. if ([[menuAnimations itemAtIndex:i] state] == NSOnState) {
  235. lastLEDMode = [menuAnimations itemAtIndex:i];
  236. }
  237. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  238. }
  239. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  240. if ([[menuVisualizations itemAtIndex:i] state] == NSOnState) {
  241. lastLEDMode = [menuVisualizations itemAtIndex:i];
  242. }
  243. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  244. }
  245. // Turn on "off" menu item
  246. [sender setState:NSOnState];
  247. // Store changed value in preferences
  248. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  249. [store setObject:@"" forKey:PREF_LED_MODE];
  250. [store synchronize];
  251. #ifdef DEBUG
  252. NSLog(@"Stored new mode: \"off\"!\n");
  253. #endif
  254. // Send command to turn off LEDs
  255. if ([serial isOpen]) {
  256. [serial sendString:@"RGB 0 0 0\n"];
  257. }
  258. } else {
  259. // Try to restore last LED setting
  260. if (lastLEDMode != nil) {
  261. [self selectedVisualization:lastLEDMode];
  262. }
  263. }
  264. }
  265. - (IBAction)toggleLights:(NSMenuItem *)sender {
  266. if ([sender state] == NSOffState) {
  267. // Turn on lights
  268. if ([serial isOpen]) {
  269. [serial sendString:@"UV 1\n"];
  270. }
  271. [sender setState:NSOnState];
  272. } else {
  273. // Turn off lights
  274. if ([serial isOpen]) {
  275. [serial sendString:@"UV 0\n"];
  276. }
  277. [sender setState:NSOffState];
  278. }
  279. // Store changed value in preferences
  280. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  281. [store setBool:([sender state] == NSOnState) forKey:PREF_LIGHTS_STATE];
  282. [store synchronize];
  283. }
  284. - (void)visualizeGPUUsage:(NSTimer *)timer {
  285. NSNumber *usage;
  286. NSNumber *freeVRAM;
  287. NSNumber *usedVRAM;
  288. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  289. NSLog(@"Error reading GPU information\n");
  290. } else {
  291. double h = [self map:[usage doubleValue] FromMin:0.0 FromMax:100.0 ToMin:GPU_COLOR_MIN ToMax:GPU_COLOR_MAX];
  292. #ifdef DEBUG
  293. NSLog(@"GPU Usage: %.3f%%\n", [usage doubleValue]);
  294. #endif
  295. unsigned char r, g, b;
  296. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  297. if ([serial isOpen]) {
  298. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", r, g, b]];
  299. }
  300. }
  301. }
  302. - (void)visualizeVRAMUsage:(NSTimer *)timer {
  303. NSNumber *usage;
  304. NSNumber *freeVRAM;
  305. NSNumber *usedVRAM;
  306. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  307. NSLog(@"Error reading GPU information\n");
  308. } else {
  309. double h = [self map:[freeVRAM doubleValue] FromMin:0.0 FromMax:([freeVRAM doubleValue] + [usedVRAM doubleValue]) ToMin:RAM_COLOR_MIN ToMax:RAM_COLOR_MAX];
  310. #ifdef DEBUG
  311. NSLog(@"VRAM %.2fGB Free + %.2fGB Used = %.2fGB mapped to color %.2f!\n", [freeVRAM doubleValue] / (1024.0 * 1024.0 * 1024.0), [usedVRAM doubleValue] / (1024.0 * 1024.0 * 1024.0), ([freeVRAM doubleValue] + [usedVRAM doubleValue]) / (1024.0 * 1024.0 * 1024.0), h);
  312. #endif
  313. unsigned char r, g, b;
  314. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  315. if ([serial isOpen]) {
  316. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", r, g, b]];
  317. }
  318. }
  319. }
  320. - (void)visualizeCPUUsage:(NSTimer *)timer {
  321. JSKMCPUUsageInfo cpuUsageInfo = [JSKSystemMonitor systemMonitor].cpuUsageInfo;
  322. double h = [self map:cpuUsageInfo.usage FromMin:0.0 FromMax:100.0 ToMin:CPU_COLOR_MIN ToMax:CPU_COLOR_MAX];
  323. #ifdef DEBUG
  324. NSLog(@"CPU Usage: %.3f%%\n", cpuUsageInfo.usage);
  325. #endif
  326. unsigned char r, g, b;
  327. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  328. if ([serial isOpen]) {
  329. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", r, g, b]];
  330. }
  331. }
  332. - (void)visualizeRAMUsage:(NSTimer *)timer {
  333. JSKMMemoryUsageInfo memoryUsageInfo = [JSKSystemMonitor systemMonitor].memoryUsageInfo;
  334. double h = [self map:memoryUsageInfo.freeMemory FromMin:0.0 FromMax:(memoryUsageInfo.usedMemory + memoryUsageInfo.freeMemory) ToMin:RAM_COLOR_MIN ToMax:RAM_COLOR_MAX];
  335. #ifdef DEBUG
  336. NSLog(@"RAM %.2fGB Free + %.2fGB Used = %.2fGB mapped to color %.2f!\n", memoryUsageInfo.freeMemory / (1024.0 * 1024.0 * 1024.0), memoryUsageInfo.usedMemory / (1024.0 * 1024.0 * 1024.0), (memoryUsageInfo.freeMemory + memoryUsageInfo.usedMemory) / (1024.0 * 1024.0 * 1024.0), h);
  337. #endif
  338. unsigned char r, g, b;
  339. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  340. if ([serial isOpen]) {
  341. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", r, g, b]];
  342. }
  343. }
  344. - (void)visualizeGPUTemperature:(NSTimer *)timer {
  345. JSKSMC *smc = [JSKSMC smc];
  346. double temp = [smc temperatureInCelsiusForKey:KEY_GPU_TEMPERATURE];
  347. if (temp > 1000.0) {
  348. temp /= 1000.0;
  349. }
  350. if (temp > GPU_TEMP_MAX) {
  351. temp = GPU_TEMP_MAX;
  352. }
  353. if (temp < GPU_TEMP_MIN) {
  354. temp = GPU_TEMP_MIN;
  355. }
  356. double h = [self map:temp FromMin:GPU_TEMP_MIN FromMax:GPU_TEMP_MAX ToMin:GPU_COLOR_MIN ToMax:GPU_COLOR_MAX];
  357. #ifdef DEBUG
  358. NSLog(@"GPU Temp %.2f mapped to color %.2f!\n", temp, h);
  359. #endif
  360. unsigned char r, g, b;
  361. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  362. if ([serial isOpen]) {
  363. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", r, g, b]];
  364. }
  365. }
  366. - (void)visualizeCPUTemperature:(NSTimer *)timer {
  367. JSKSMC *smc = [JSKSMC smc];
  368. double temp = [smc temperatureInCelsiusForKey:KEY_CPU_TEMPERATURE];
  369. if (temp > 1000.0) {
  370. temp /= 1000.0;
  371. }
  372. if (temp > CPU_TEMP_MAX) {
  373. temp = CPU_TEMP_MAX;
  374. }
  375. if (temp < CPU_TEMP_MIN) {
  376. temp = CPU_TEMP_MIN;
  377. }
  378. double h = [self map:temp FromMin:CPU_TEMP_MIN FromMax:CPU_TEMP_MAX ToMin:CPU_COLOR_MIN ToMax:CPU_COLOR_MAX];
  379. #ifdef DEBUG
  380. NSLog(@"CPU Temp %.2f mapped to color %.2f!\n", temp, h);
  381. #endif
  382. unsigned char r, g, b;
  383. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  384. if ([serial isOpen]) {
  385. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", r, g, b]];
  386. }
  387. }
  388. - (void)visualizeRGBFade:(NSTimer *)timer {
  389. static unsigned char color[3] = { 255, 0, 0 };
  390. static int dec = 0;
  391. static int val = 0;
  392. // Adapted from:
  393. // https://gist.github.com/jamesotron/766994
  394. if (dec < 3) {
  395. int inc = (dec == 2) ? 0 : (dec + 1);
  396. if (val < 255) {
  397. color[dec] -= 1;
  398. color[inc] += 1;
  399. val++;
  400. } else {
  401. val = 0;
  402. dec++;
  403. }
  404. } else {
  405. dec = 0;
  406. }
  407. if ([serial isOpen]) {
  408. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", color[0], color[1], color[2]]];
  409. }
  410. }
  411. - (void)visualizeHSVFade:(NSTimer *)timer {
  412. static float h = 0.0;
  413. if (h < 360.0) {
  414. h += 0.5;
  415. } else {
  416. h = 0.0;
  417. }
  418. unsigned char r, g, b;
  419. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  420. if ([serial isOpen]) {
  421. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", r, g, b]];
  422. }
  423. }
  424. - (void)visualizeRandom:(NSTimer *)timer {
  425. if ([serial isOpen]) {
  426. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", rand() % 256, rand() % 256, rand() % 256]];
  427. }
  428. }
  429. - (BOOL)timedVisualization:(NSString *)mode {
  430. // Stop previous timer setting
  431. if (animation != nil) {
  432. [animation invalidate];
  433. animation = nil;
  434. }
  435. // Schedule next invocation for this animation...
  436. if ([mode isEqualToString:TEXT_GPU_USAGE]) {
  437. animation = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(visualizeGPUUsage:) userInfo:mode repeats:YES];
  438. } else if ([mode isEqualToString:TEXT_VRAM_USAGE]) {
  439. animation = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(visualizeVRAMUsage:) userInfo:mode repeats:YES];
  440. } else if ([mode isEqualToString:TEXT_CPU_USAGE]) {
  441. animation = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(visualizeCPUUsage:) userInfo:mode repeats:YES];
  442. } else if ([mode isEqualToString:TEXT_RAM_USAGE]) {
  443. animation = [NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:@selector(visualizeRAMUsage:) userInfo:mode repeats:YES];
  444. } else if ([mode isEqualToString:TEXT_CPU_TEMPERATURE]) {
  445. animation = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(visualizeCPUTemperature:) userInfo:mode repeats:YES];
  446. } else if ([mode isEqualToString:TEXT_GPU_TEMPERATURE]) {
  447. animation = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(visualizeGPUTemperature:) userInfo:mode repeats:YES];
  448. } else if ([mode isEqualToString:TEXT_RGB_FADE]) {
  449. animation = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(visualizeRGBFade:) userInfo:mode repeats:YES];
  450. } else if ([mode isEqualToString:TEXT_HSV_FADE]) {
  451. animation = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(visualizeHSVFade:) userInfo:mode repeats:YES];
  452. } else if ([mode isEqualToString:TEXT_RANDOM]) {
  453. animation = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(visualizeRandom:) userInfo:mode repeats:YES];
  454. } else {
  455. return NO;
  456. }
  457. #ifdef DEBUG
  458. NSLog(@"Scheduled animation for \"%@\"!\n", mode);
  459. #endif
  460. // ...and also execute it right now
  461. [animation fire];
  462. return YES;
  463. }
  464. - (void)selectedVisualization:(NSMenuItem *)sender {
  465. // Turn off all other LED menu items
  466. if (menuColors != nil) {
  467. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  468. [[menuColors itemAtIndex:i] setState:NSOffState];
  469. }
  470. }
  471. if (menuAnimations != nil) {
  472. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  473. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  474. }
  475. }
  476. if (menuVisualizations != nil) {
  477. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  478. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  479. }
  480. }
  481. [buttonOff setState:NSOffState];
  482. [sender setState:NSOnState];
  483. // Check if a static color was selected
  484. BOOL found = NO;
  485. if (staticColors != nil) {
  486. for (NSString *key in [staticColors allKeys]) {
  487. if ([sender.title isEqualToString:key]) {
  488. found = YES;
  489. NSColor *color = [staticColors valueForKey:key];
  490. unsigned char red = [color redComponent] * 255;
  491. unsigned char green = [color greenComponent] * 255;
  492. unsigned char blue = [color blueComponent] * 255;
  493. NSString *string = [NSString stringWithFormat:@"RGB %d %d %d\n", red, green, blue];
  494. if ([serial isOpen]) {
  495. [serial sendString:string];
  496. }
  497. break;
  498. }
  499. }
  500. }
  501. if (!found) {
  502. // Check if an animated visualization was selected
  503. if ([self timedVisualization:[sender title]] == NO) {
  504. NSLog(@"Unknown LED Visualization selected!\n");
  505. return;
  506. }
  507. }
  508. // Store changed value in preferences
  509. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  510. [store setObject:[sender title] forKey:PREF_LED_MODE];
  511. [store synchronize];
  512. #ifdef DEBUG
  513. NSLog(@"Stored new mode: \"%@\"!\n", [sender title]);
  514. #endif
  515. }
  516. - (void)selectedSerialPort:(NSMenuItem *)source {
  517. // Store selection for next start-up
  518. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  519. [store setObject:[source title] forKey:PREF_SERIAL_PORT];
  520. [store synchronize];
  521. // De-select all other ports
  522. for (int i = 0; i < [menuPorts numberOfItems]; i++) {
  523. [[menuPorts itemAtIndex:i] setState:NSOffState];
  524. }
  525. // Select only the current port
  526. [source setState:NSOnState];
  527. // Close previously opened port, if any
  528. if ([serial isOpen]) {
  529. [serial closePort];
  530. }
  531. // Try to open selected port
  532. [serial setPortName:[source title]];
  533. if ([serial openPort] != 0) {
  534. [source setState:NSOffState];
  535. } else {
  536. // Restore the current configuration
  537. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  538. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  539. [self selectedVisualization:[menuColors itemAtIndex:i]];
  540. }
  541. }
  542. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  543. if ([[menuAnimations itemAtIndex:i] state] == NSOnState) {
  544. [self selectedVisualization:[menuAnimations itemAtIndex:i]];
  545. }
  546. }
  547. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  548. if ([[menuVisualizations itemAtIndex:i] state] == NSOnState) {
  549. [self selectedVisualization:[menuVisualizations itemAtIndex:i]];
  550. }
  551. }
  552. if ([buttonOff state] == NSOnState) {
  553. [buttonOff setState:NSOffState];
  554. [self turnLEDsOff:buttonOff];
  555. }
  556. if ([buttonLights state] == NSOnState) {
  557. [serial sendString:@"UV 1\n"];
  558. } else {
  559. [serial sendString:@"UV 0\n"];
  560. }
  561. }
  562. }
  563. - (IBAction)showAbout:(id)sender {
  564. [NSApp activateIgnoringOtherApps:YES];
  565. [application orderFrontStandardAboutPanel:self];
  566. }
  567. - (double)map:(double)val FromMin:(double)fmin FromMax:(double)fmax ToMin:(double)tmin ToMax:(double)tmax {
  568. double norm = (val - fmin) / (fmax - fmin);
  569. return (norm * (tmax - tmin)) + tmin;
  570. }
  571. - (void)convertH:(double)h S:(double)s V:(double)v toR:(unsigned char *)r G:(unsigned char *)g B:(unsigned char *)b {
  572. // Adapted from:
  573. // https://gist.github.com/hdznrrd/656996
  574. if (s == 0.0) {
  575. // Achromatic
  576. *r = *g = *b = (unsigned char)(v * 255);
  577. return;
  578. }
  579. h /= 60; // sector 0 to 5
  580. int i = floor(h);
  581. double f = h - i; // factorial part of h
  582. double p = v * (1 - s);
  583. double q = v * (1 - s * f);
  584. double t = v * (1 - s * (1 - f));
  585. switch (i) {
  586. case 0:
  587. *r = (unsigned char)round(255 * v);
  588. *g = (unsigned char)round(255 * t);
  589. *b = (unsigned char)round(255 * p);
  590. break;
  591. case 1:
  592. *r = (unsigned char)round(255 * q);
  593. *g = (unsigned char)round(255 * v);
  594. *b = (unsigned char)round(255 * p);
  595. break;
  596. case 2:
  597. *r = (unsigned char)round(255 * p);
  598. *g = (unsigned char)round(255 * v);
  599. *b = (unsigned char)round(255 * t);
  600. break;
  601. case 3:
  602. *r = (unsigned char)round(255 * p);
  603. *g = (unsigned char)round(255 * q);
  604. *b = (unsigned char)round(255 * v);
  605. break;
  606. case 4:
  607. *r = (unsigned char)round(255 * t);
  608. *g = (unsigned char)round(255 * p);
  609. *b = (unsigned char)round(255 * v);
  610. break;
  611. default: case 5:
  612. *r = (unsigned char)round(255 * v);
  613. *g = (unsigned char)round(255 * p);
  614. *b = (unsigned char)round(255 * q);
  615. break;
  616. }
  617. }
  618. @end