Simple RGB LED controller for Mac OS X
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

AppDelegate.m 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975
  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 "Screenshot.h"
  12. #import "SystemInfoKit/SystemInfoKit.h"
  13. #define PREF_SERIAL_PORT @"SerialPort"
  14. #define PREF_LIGHTS_STATE @"LightState"
  15. #define PREF_LED_MODE @"LEDMode"
  16. #define PREF_BRIGHTNESS @"Brightness"
  17. #define PREF_COLOR @"ManualColor"
  18. #define TEXT_MANUAL @"Select..."
  19. #define TEXT_CPU_USAGE @"CPU Usage"
  20. #define TEXT_RAM_USAGE @"RAM Usage"
  21. #define TEXT_GPU_USAGE @"GPU Usage"
  22. #define TEXT_VRAM_USAGE @"VRAM Usage"
  23. #define TEXT_CPU_TEMPERATURE @"CPU Temperature"
  24. #define TEXT_GPU_TEMPERATURE @"GPU Temperature"
  25. #define TEXT_RGB_FADE @"RGB Fade"
  26. #define TEXT_HSV_FADE @"HSV Fade"
  27. #define TEXT_RANDOM @"Random"
  28. #define KEY_CPU_TEMPERATURE @"TC0D"
  29. #define KEY_GPU_TEMPERATURE @"TG0D"
  30. // Temperature in Celsius
  31. #define CPU_TEMP_MIN 20
  32. #define CPU_TEMP_MAX 90
  33. // HSV Color (S = V = 1)
  34. #define CPU_COLOR_MIN 120
  35. #define CPU_COLOR_MAX 0
  36. #define GPU_TEMP_MIN 20
  37. #define GPU_TEMP_MAX 90
  38. #define GPU_COLOR_MIN 120
  39. #define GPU_COLOR_MAX 0
  40. #define RAM_COLOR_MIN 0
  41. #define RAM_COLOR_MAX 120
  42. // You can play around with these values (skipped pixels, display timer delay) to change CPU usage in display mode
  43. #define AVERAGE_COLOR_PERFORMANCE_INC 10
  44. #define DISPLAY_DELAY 0.1
  45. @interface AppDelegate ()
  46. @property (strong) NSStatusItem *statusItem;
  47. @property (strong) NSImage *statusImage;
  48. @property (strong) NSDictionary *staticColors;
  49. @property (strong) NSTimer *animation;
  50. @property (strong) Serial *serial;
  51. @property (strong) NSMenuItem *lastLEDMode;
  52. @end
  53. @implementation AppDelegate
  54. @synthesize statusMenu, application;
  55. @synthesize menuColors, menuAnimations, menuVisualizations, menuPorts;
  56. @synthesize menuItemDisplays, menuDisplays;
  57. @synthesize buttonOff, buttonLights;
  58. @synthesize brightnessItem, brightnessSlider, brightnessLabel;
  59. @synthesize statusItem, statusImage;
  60. @synthesize staticColors, animation;
  61. @synthesize serial, lastLEDMode;
  62. @synthesize menuItemColor;
  63. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
  64. srand((unsigned)time(NULL));
  65. serial = [[Serial alloc] init];
  66. lastLEDMode = nil;
  67. animation = nil;
  68. // Prepare status bar menu
  69. statusImage = [NSImage imageNamed:@"MenuIcon"];
  70. [statusImage setTemplate:YES];
  71. statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
  72. [statusItem setImage:statusImage];
  73. [statusItem setMenu:statusMenu];
  74. // Set default configuration values, load existing ones
  75. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  76. NSMutableDictionary *appDefaults = [NSMutableDictionary dictionaryWithObject:@"" forKey:PREF_SERIAL_PORT];
  77. [appDefaults setObject:[NSNumber numberWithBool:NO] forKey:PREF_LIGHTS_STATE];
  78. [appDefaults setObject:@"" forKey:PREF_LED_MODE];
  79. [appDefaults setObject:[NSNumber numberWithFloat:50.0] forKey:PREF_BRIGHTNESS];
  80. [store registerDefaults:appDefaults];
  81. [store synchronize];
  82. NSString *savedPort = [store stringForKey:PREF_SERIAL_PORT];
  83. BOOL turnOnLights = [store boolForKey:PREF_LIGHTS_STATE];
  84. NSString *lastMode = [store stringForKey:PREF_LED_MODE];
  85. float brightness = [store floatForKey:PREF_BRIGHTNESS];
  86. NSData *lastColorData = [store dataForKey:PREF_COLOR];
  87. NSColor *lastColor = nil;
  88. if (lastColorData != nil) {
  89. lastColor = (NSColor *)[NSUnarchiver unarchiveObjectWithData:lastColorData];
  90. }
  91. // Prepare brightness menu
  92. brightnessItem.view = brightnessSlider;
  93. [brightnessSlider setFloatValue:brightness];
  94. [brightnessLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", brightness]];
  95. // Prepare serial port menu
  96. NSArray *ports = [Serial listSerialPorts];
  97. if ([ports count] > 0) {
  98. [menuPorts removeAllItems];
  99. for (int i = 0; i < [ports count]; i++) {
  100. // Add Menu Item for this port
  101. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
  102. [item setTag:-1];
  103. [menuPorts addItem:item];
  104. // Set Enabled if it was used the last time
  105. if ((savedPort != nil) && [[ports objectAtIndex:i] isEqualToString:savedPort]) {
  106. [[menuPorts itemAtIndex:i] setState:NSOnState];
  107. // Try to open serial port
  108. [serial setPortName:savedPort];
  109. if ([serial openPort]) {
  110. // Unselect it when an error occured opening the port
  111. [[menuPorts itemAtIndex:i] setState:NSOffState];
  112. }
  113. }
  114. }
  115. }
  116. // Select "Off" button if it was last selected
  117. if ([lastMode isEqualToString:@""]) {
  118. [buttonOff setState:NSOffState];
  119. [self turnLEDsOff:buttonOff];
  120. }
  121. // Prepare static colors menu
  122. staticColors = [NSDictionary dictionaryWithObjectsAndKeys:
  123. [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:0.0f alpha:0.0f], @"Red",
  124. [NSColor colorWithCalibratedRed:0.0f green:1.0f blue:0.0f alpha:0.0f], @"Green",
  125. [NSColor colorWithCalibratedRed:0.0f green:0.0f blue:1.0f alpha:0.0f], @"Blue",
  126. [NSColor colorWithCalibratedRed:0.0f green:1.0f blue:1.0f alpha:0.0f], @"Cyan",
  127. [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:1.0f alpha:0.0f], @"Magenta",
  128. [NSColor colorWithCalibratedRed:1.0f green:1.0f blue:0.0f alpha:0.0f], @"Yellow",
  129. [NSColor colorWithCalibratedRed:1.0f green:1.0f blue:1.0f alpha:0.0f], @"White",
  130. nil];
  131. for (NSString *key in [staticColors allKeys]) {
  132. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
  133. [item setTag:-1];
  134. if ([key isEqualToString:lastMode]) {
  135. [self selectedVisualization:item];
  136. }
  137. [menuColors addItem:item];
  138. }
  139. menuItemColor = [[NSMenuItem alloc] initWithTitle:TEXT_MANUAL action:@selector(setColorSelected:) keyEquivalent:@""];
  140. if ([lastMode isEqualToString:TEXT_MANUAL]) {
  141. if (lastColor != nil) {
  142. // Restore previously set RGB color
  143. [self setLightsColor:lastColor];
  144. }
  145. [menuItemColor setState:NSOnState];
  146. }
  147. [menuItemColor setTag:-1];
  148. [menuColors addItem:menuItemColor];
  149. // Prepare animations menu
  150. NSArray *animationStrings = [NSArray arrayWithObjects:
  151. TEXT_RGB_FADE,
  152. TEXT_HSV_FADE,
  153. TEXT_RANDOM,
  154. nil];
  155. for (NSString *key in animationStrings) {
  156. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
  157. [item setTag:-1];
  158. if ([key isEqualToString:lastMode]) {
  159. [self selectedVisualization:item];
  160. }
  161. [menuAnimations addItem:item];
  162. }
  163. // Add CPU Usage menu item
  164. NSMenuItem *cpuUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  165. [cpuUsageItem setTag:-1];
  166. if ([lastMode isEqualToString:TEXT_CPU_USAGE]) {
  167. [self selectedVisualization:cpuUsageItem];
  168. }
  169. [menuVisualizations addItem:cpuUsageItem];
  170. // Add Memory Usage item
  171. NSMenuItem *memoryUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_RAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  172. [memoryUsageItem setTag:-1];
  173. if ([lastMode isEqualToString:TEXT_RAM_USAGE]) {
  174. [self selectedVisualization:memoryUsageItem];
  175. }
  176. [menuVisualizations addItem:memoryUsageItem];
  177. // Check if GPU Stats are available, add menu items if so
  178. NSNumber *usage;
  179. NSNumber *freeVRAM;
  180. NSNumber *usedVRAM;
  181. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  182. NSLog(@"Error reading GPU information\n");
  183. } else {
  184. NSMenuItem *itemUsage = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  185. [itemUsage setTag:-1];
  186. if ([lastMode isEqualToString:TEXT_GPU_USAGE]) {
  187. [self selectedVisualization:itemUsage];
  188. }
  189. [menuVisualizations addItem:itemUsage];
  190. NSMenuItem *itemVRAM = [[NSMenuItem alloc] initWithTitle:TEXT_VRAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  191. [itemVRAM setTag:-1];
  192. if ([lastMode isEqualToString:TEXT_VRAM_USAGE]) {
  193. [self selectedVisualization:itemVRAM];
  194. }
  195. [menuVisualizations addItem:itemVRAM];
  196. }
  197. // Check available temperatures and add menu items
  198. JSKSMC *smc = [JSKSMC smc];
  199. for (int i = 0; i < [[smc workingTempKeys] count]; i++) {
  200. NSString *key = [smc.workingTempKeys objectAtIndex:i];
  201. #ifdef DEBUG
  202. NSString *name = [smc humanReadableNameForKey:key];
  203. NSLog(@"Sensor \"%@\": \"%@\"\n", key, name);
  204. #endif
  205. if ([key isEqualToString:KEY_CPU_TEMPERATURE]) {
  206. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
  207. [item setTag:-1];
  208. if ([lastMode isEqualToString:TEXT_CPU_TEMPERATURE]) {
  209. [self selectedVisualization:item];
  210. }
  211. [menuVisualizations addItem:item];
  212. }
  213. if ([key isEqualToString:KEY_GPU_TEMPERATURE]) {
  214. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
  215. [item setTag:-1];
  216. if ([lastMode isEqualToString:TEXT_GPU_TEMPERATURE]) {
  217. [self selectedVisualization:item];
  218. }
  219. [menuVisualizations addItem:item];
  220. }
  221. }
  222. // Restore previously used lights configuration
  223. if (turnOnLights) {
  224. // Turn on lights
  225. if ([serial isOpen]) {
  226. [serial sendString:@"UV 1\n"];
  227. }
  228. [buttonLights setState:NSOnState];
  229. } else {
  230. // Turn off lights
  231. if ([serial isOpen]) {
  232. [serial sendString:@"UV 0\n"];
  233. }
  234. }
  235. // List available displays and add menu items
  236. [Screenshot init:self];
  237. NSArray *displayIDs = [Screenshot listDisplays];
  238. [self updateDisplayUI:displayIDs];
  239. }
  240. - (void)applicationWillTerminate:(NSNotification *)aNotification {
  241. // Stop previous timer setting
  242. if (animation != nil) {
  243. [animation invalidate];
  244. animation = nil;
  245. }
  246. // Remove display callback
  247. [Screenshot close:self];
  248. // Turn off all lights if possible
  249. if ([serial isOpen]) {
  250. [serial sendString:@"RGB 0 0 0\n"];
  251. [serial sendString:@"UV 0\n"];
  252. [serial closePort];
  253. }
  254. }
  255. - (void)clearDisplayUI {
  256. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  257. if ([[menuDisplays itemAtIndex:i] isEnabled] == YES) {
  258. // A display configuration is currently selected. Disable the timer
  259. if (animation != nil) {
  260. [animation invalidate];
  261. animation = nil;
  262. }
  263. }
  264. }
  265. [menuDisplays removeAllItems];
  266. [menuItemDisplays setHidden:YES];
  267. }
  268. - (void)updateDisplayUI:(NSArray *)displayIDs {
  269. if ([displayIDs count] > 0) {
  270. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  271. NSString *lastMode = [store stringForKey:PREF_LED_MODE];
  272. [menuItemDisplays setHidden:NO];
  273. for (int i = 0; i < [displayIDs count]; i++) {
  274. NSString *title = [Screenshot displayNameFromDisplayID:[displayIDs objectAtIndex:i]];
  275. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title
  276. action:@selector(selectedVisualization:)
  277. keyEquivalent:@""];
  278. [item setTag:[[displayIDs objectAtIndex:i] integerValue]];
  279. if ([title isEqualToString:lastMode]) {
  280. [self selectedVisualization:item];
  281. }
  282. [menuDisplays addItem:item];
  283. }
  284. }
  285. }
  286. - (void)setLightsColor:(NSColor *)color {
  287. CGFloat red, green, blue, alpha;
  288. [color getRed:&red green:&green blue:&blue alpha:&alpha];
  289. [self setLightsR:red * 255 G:green * 255 B:blue * 255];
  290. // Stop previous timer setting
  291. if (animation != nil) {
  292. [animation invalidate];
  293. animation = nil;
  294. }
  295. // Turn off all other LED menu items
  296. if (menuColors != nil) {
  297. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  298. [[menuColors itemAtIndex:i] setState:NSOffState];
  299. }
  300. }
  301. if (menuAnimations != nil) {
  302. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  303. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  304. }
  305. }
  306. if (menuVisualizations != nil) {
  307. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  308. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  309. }
  310. }
  311. if (menuDisplays != nil) {
  312. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  313. [[menuDisplays itemAtIndex:i] setState:NSOffState];
  314. }
  315. }
  316. [buttonOff setState:NSOffState];
  317. [menuItemColor setState:NSOnState];
  318. // Store new manually selected color
  319. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  320. NSData *data = [NSArchiver archivedDataWithRootObject:color];
  321. [store setObject:data forKey:PREF_COLOR];
  322. [store setObject:TEXT_MANUAL forKey:PREF_LED_MODE];
  323. [store synchronize];
  324. }
  325. - (void)setLightsR:(unsigned char)r G:(unsigned char)g B:(unsigned char)b {
  326. if ([serial isOpen]) {
  327. unsigned char red = r * ([brightnessSlider floatValue] / 100.0);
  328. unsigned char green = g * ([brightnessSlider floatValue] / 100.0);
  329. unsigned char blue = b * ([brightnessSlider floatValue] / 100.0);
  330. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", red, green, blue]];
  331. } else {
  332. #ifdef DEBUG
  333. NSLog(@"Trying to send RGB without opened port!\n");
  334. #endif
  335. }
  336. }
  337. - (IBAction)relistSerialPorts:(id)sender {
  338. // Refill port list
  339. NSArray *ports = [Serial listSerialPorts];
  340. [menuPorts removeAllItems];
  341. for (int i = 0; i < [ports count]; i++) {
  342. // Add Menu Item for this port
  343. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
  344. [item setTag:-1];
  345. [menuPorts addItem:item];
  346. // Mark it if it is currently open
  347. if ([serial isOpen]) {
  348. if ([[ports objectAtIndex:i] isEqualToString:[serial portName]]) {
  349. [[menuPorts itemAtIndex:i] setState:NSOnState];
  350. }
  351. }
  352. }
  353. }
  354. - (void)setColorSelected:(NSMenuItem *)sender {
  355. NSColorPanel *cp = [NSColorPanel sharedColorPanel];
  356. [cp setTarget:self];
  357. [cp setAction:@selector(colorSelected:)];
  358. [cp setShowsAlpha:NO];
  359. [cp setContinuous:NO];
  360. [cp setMode:NSRGBModeColorPanel];
  361. // Try to restore last manually selected color
  362. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  363. NSData *lastColorData = [store dataForKey:PREF_COLOR];
  364. NSColor *lastColor = nil;
  365. if (lastColorData != nil) {
  366. lastColor = (NSColor *)[NSUnarchiver unarchiveObjectWithData:lastColorData];
  367. [cp setColor:lastColor];
  368. }
  369. [NSApp activateIgnoringOtherApps:YES];
  370. [application orderFrontColorPanel:cp];
  371. }
  372. - (void)colorSelected:(id)sender {
  373. [self setLightsColor:[sender color]];
  374. }
  375. - (IBAction)brightnessMoved:(NSSlider *)sender {
  376. [brightnessLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", [sender floatValue]]];
  377. // Restore the current configuration for items where it won't happen automatically
  378. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  379. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  380. [self selectedVisualization:[menuColors itemAtIndex:i]];
  381. }
  382. }
  383. // Store changed value in preferences
  384. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  385. [store setObject:[NSNumber numberWithFloat:[sender floatValue]] forKey:PREF_BRIGHTNESS];
  386. [store synchronize];
  387. }
  388. - (IBAction)turnLEDsOff:(NSMenuItem *)sender {
  389. if ([sender state] == NSOffState) {
  390. lastLEDMode = nil;
  391. // Stop previous timer setting
  392. if (animation != nil) {
  393. [animation invalidate];
  394. animation = nil;
  395. }
  396. // Turn off all other LED menu items
  397. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  398. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  399. lastLEDMode = [menuColors itemAtIndex:i];
  400. }
  401. [[menuColors itemAtIndex:i] setState:NSOffState];
  402. }
  403. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  404. if ([[menuAnimations itemAtIndex:i] state] == NSOnState) {
  405. lastLEDMode = [menuAnimations itemAtIndex:i];
  406. }
  407. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  408. }
  409. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  410. if ([[menuVisualizations itemAtIndex:i] state] == NSOnState) {
  411. lastLEDMode = [menuVisualizations itemAtIndex:i];
  412. }
  413. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  414. }
  415. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  416. if ([[menuDisplays itemAtIndex:i] state] == NSOnState) {
  417. lastLEDMode = [menuDisplays itemAtIndex:i];
  418. }
  419. [[menuDisplays itemAtIndex:i] setState:NSOffState];
  420. }
  421. // Turn on "off" menu item
  422. [sender setState:NSOnState];
  423. // Store changed value in preferences
  424. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  425. [store setObject:@"" forKey:PREF_LED_MODE];
  426. [store synchronize];
  427. #ifdef DEBUG
  428. NSLog(@"Stored new mode: \"off\"!\n");
  429. #endif
  430. // Send command to turn off LEDs
  431. [self setLightsR:0 G:0 B:0];
  432. } else {
  433. // Try to restore last LED setting
  434. if (lastLEDMode != nil) {
  435. [self selectedVisualization:lastLEDMode];
  436. }
  437. }
  438. }
  439. - (IBAction)toggleLights:(NSMenuItem *)sender {
  440. if ([sender state] == NSOffState) {
  441. // Turn on lights
  442. if ([serial isOpen]) {
  443. [serial sendString:@"UV 1\n"];
  444. }
  445. [sender setState:NSOnState];
  446. } else {
  447. // Turn off lights
  448. if ([serial isOpen]) {
  449. [serial sendString:@"UV 0\n"];
  450. }
  451. [sender setState:NSOffState];
  452. }
  453. // Store changed value in preferences
  454. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  455. [store setBool:([sender state] == NSOnState) forKey:PREF_LIGHTS_STATE];
  456. [store synchronize];
  457. }
  458. - (BOOL)timedVisualization:(NSString *)mode {
  459. // Stop previous timer setting
  460. if (animation != nil) {
  461. [animation invalidate];
  462. animation = nil;
  463. }
  464. // Schedule next invocation for this animation...
  465. if ([mode isEqualToString:TEXT_GPU_USAGE]) {
  466. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeGPUUsage:) userInfo:mode repeats:YES];
  467. } else if ([mode isEqualToString:TEXT_VRAM_USAGE]) {
  468. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeVRAMUsage:) userInfo:mode repeats:YES];
  469. } else if ([mode isEqualToString:TEXT_CPU_USAGE]) {
  470. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeCPUUsage:) userInfo:mode repeats:YES];
  471. } else if ([mode isEqualToString:TEXT_RAM_USAGE]) {
  472. animation = [NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:@selector(visualizeRAMUsage:) userInfo:mode repeats:YES];
  473. } else if ([mode isEqualToString:TEXT_CPU_TEMPERATURE]) {
  474. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeCPUTemperature:) userInfo:mode repeats:YES];
  475. } else if ([mode isEqualToString:TEXT_GPU_TEMPERATURE]) {
  476. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeGPUTemperature:) userInfo:mode repeats:YES];
  477. } else if ([mode isEqualToString:TEXT_RGB_FADE]) {
  478. animation = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(visualizeRGBFade:) userInfo:mode repeats:YES];
  479. } else if ([mode isEqualToString:TEXT_HSV_FADE]) {
  480. animation = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(visualizeHSVFade:) userInfo:mode repeats:YES];
  481. } else if ([mode isEqualToString:TEXT_RANDOM]) {
  482. animation = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(visualizeRandom:) userInfo:mode repeats:YES];
  483. } else {
  484. return NO;
  485. }
  486. #ifdef DEBUG
  487. NSLog(@"Scheduled animation for \"%@\"!\n", mode);
  488. #endif
  489. // ...and also execute it right now
  490. [animation fire];
  491. return YES;
  492. }
  493. - (void)displayVisualization:(NSMenuItem *)sender {
  494. // Stop previous timer setting
  495. if (animation != nil) {
  496. [animation invalidate];
  497. animation = nil;
  498. }
  499. // Schedule next invocation for this animation...
  500. animation = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:[NSNumber numberWithInteger:[sender tag]] repeats:YES];
  501. // ...and also execute it right now
  502. [animation fire];
  503. }
  504. - (void)selectedVisualization:(NSMenuItem *)sender {
  505. // Turn off all other LED menu items
  506. if (menuColors != nil) {
  507. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  508. [[menuColors itemAtIndex:i] setState:NSOffState];
  509. }
  510. }
  511. if (menuAnimations != nil) {
  512. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  513. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  514. }
  515. }
  516. if (menuVisualizations != nil) {
  517. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  518. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  519. }
  520. }
  521. if (menuDisplays != nil) {
  522. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  523. [[menuDisplays itemAtIndex:i] setState:NSOffState];
  524. }
  525. }
  526. [buttonOff setState:NSOffState];
  527. [sender setState:NSOnState];
  528. // Check if it is a display
  529. BOOL found = NO;
  530. if ([sender tag] > -1) {
  531. found = YES;
  532. [self displayVisualization:sender];
  533. }
  534. // Check if it is the manual color select item
  535. if ([sender.title isEqualToString:TEXT_MANUAL]) {
  536. found = YES;
  537. [self colorSelected:[NSColorPanel sharedColorPanel]];
  538. }
  539. // Check if a static color was selected
  540. if ((found == NO) && (staticColors != nil)) {
  541. for (NSString *key in [staticColors allKeys]) {
  542. if ([sender.title isEqualToString:key]) {
  543. found = YES;
  544. // Stop previous timer setting
  545. if (animation != nil) {
  546. [animation invalidate];
  547. animation = nil;
  548. }
  549. NSColor *color = [staticColors valueForKey:key];
  550. unsigned char red = [color redComponent] * 255;
  551. unsigned char green = [color greenComponent] * 255;
  552. unsigned char blue = [color blueComponent] * 255;
  553. [self setLightsR:red G:green B:blue];
  554. break;
  555. }
  556. }
  557. }
  558. if (found == NO) {
  559. // Check if an animated visualization was selected
  560. if ([self timedVisualization:[sender title]] == NO) {
  561. NSLog(@"Unknown LED Visualization selected!\n");
  562. return;
  563. }
  564. }
  565. // Store changed value in preferences
  566. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  567. [store setObject:[sender title] forKey:PREF_LED_MODE];
  568. [store synchronize];
  569. #ifdef DEBUG
  570. NSLog(@"Stored new mode: \"%@\"!\n", [sender title]);
  571. #endif
  572. }
  573. - (void)selectedSerialPort:(NSMenuItem *)source {
  574. // Store selection for next start-up
  575. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  576. [store setObject:[source title] forKey:PREF_SERIAL_PORT];
  577. [store synchronize];
  578. // De-select all other ports
  579. for (int i = 0; i < [menuPorts numberOfItems]; i++) {
  580. [[menuPorts itemAtIndex:i] setState:NSOffState];
  581. }
  582. // Select only the current port
  583. [source setState:NSOnState];
  584. // Close previously opened port, if any
  585. if ([serial isOpen]) {
  586. [serial closePort];
  587. }
  588. // Try to open selected port
  589. [serial setPortName:[source title]];
  590. if ([serial openPort] != 0) {
  591. [source setState:NSOffState];
  592. } else {
  593. // Restore the current configuration
  594. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  595. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  596. [self selectedVisualization:[menuColors itemAtIndex:i]];
  597. }
  598. }
  599. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  600. if ([[menuAnimations itemAtIndex:i] state] == NSOnState) {
  601. [self selectedVisualization:[menuAnimations itemAtIndex:i]];
  602. }
  603. }
  604. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  605. if ([[menuVisualizations itemAtIndex:i] state] == NSOnState) {
  606. [self selectedVisualization:[menuVisualizations itemAtIndex:i]];
  607. }
  608. }
  609. if ([buttonOff state] == NSOnState) {
  610. [buttonOff setState:NSOffState];
  611. [self turnLEDsOff:buttonOff];
  612. }
  613. if ([buttonLights state] == NSOnState) {
  614. [serial sendString:@"UV 1\n"];
  615. } else {
  616. [serial sendString:@"UV 0\n"];
  617. }
  618. }
  619. }
  620. - (IBAction)showAbout:(id)sender {
  621. [NSApp activateIgnoringOtherApps:YES];
  622. [application orderFrontStandardAboutPanel:self];
  623. }
  624. // ------------------------------------------------------
  625. // ------------------- Visualizations -------------------
  626. // ------------------------------------------------------
  627. - (void)visualizeDisplay:(NSTimer *)timer {
  628. NSBitmapImageRep *screen = [Screenshot screenshot:[timer userInfo]];
  629. NSInteger spp = [screen samplesPerPixel];
  630. if (((spp != 3) && (spp != 4)) || ([screen isPlanar] == YES) || ([screen numberOfPlanes] != 1)) {
  631. NSLog(@"Unknown image format (%ld, %c, %ld)!\n", (long)spp, ([screen isPlanar] == YES) ? 'p' : 'n', (long)[screen numberOfPlanes]);
  632. return;
  633. }
  634. int redC = 0, greenC = 1, blueC = 2;
  635. if ([screen bitmapFormat] & NSAlphaFirstBitmapFormat) {
  636. redC = 1; greenC = 2; blueC = 3;
  637. }
  638. unsigned char *data = [screen bitmapData];
  639. unsigned long width = [screen pixelsWide];
  640. unsigned long height = [screen pixelsHigh];
  641. unsigned long max = width * height;
  642. unsigned long red = 0, green = 0, blue = 0;
  643. for (unsigned long i = 0; i < max; i += AVERAGE_COLOR_PERFORMANCE_INC) {
  644. unsigned long off = spp * i;
  645. red += data[off + redC];
  646. green += data[off + greenC];
  647. blue += data[off + blueC];
  648. }
  649. max /= AVERAGE_COLOR_PERFORMANCE_INC;
  650. [self setLightsR:(red / max) G:(green / max) B:(blue / max)];
  651. }
  652. - (void)visualizeGPUUsage:(NSTimer *)timer {
  653. NSNumber *usage;
  654. NSNumber *freeVRAM;
  655. NSNumber *usedVRAM;
  656. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  657. NSLog(@"Error reading GPU information\n");
  658. } else {
  659. double h = [self map:[usage doubleValue] FromMin:0.0 FromMax:100.0 ToMin:GPU_COLOR_MIN ToMax:GPU_COLOR_MAX];
  660. #ifdef DEBUG
  661. NSLog(@"GPU Usage: %.3f%%\n", [usage doubleValue]);
  662. #endif
  663. unsigned char r, g, b;
  664. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  665. [self setLightsR:r G:g B:b];
  666. }
  667. }
  668. - (void)visualizeVRAMUsage:(NSTimer *)timer {
  669. NSNumber *usage;
  670. NSNumber *freeVRAM;
  671. NSNumber *usedVRAM;
  672. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  673. NSLog(@"Error reading GPU information\n");
  674. } else {
  675. double h = [self map:[freeVRAM doubleValue] FromMin:0.0 FromMax:([freeVRAM doubleValue] + [usedVRAM doubleValue]) ToMin:RAM_COLOR_MIN ToMax:RAM_COLOR_MAX];
  676. #ifdef DEBUG
  677. 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);
  678. #endif
  679. unsigned char r, g, b;
  680. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  681. [self setLightsR:r G:g B:b];
  682. }
  683. }
  684. - (void)visualizeCPUUsage:(NSTimer *)timer {
  685. JSKMCPUUsageInfo cpuUsageInfo = [JSKSystemMonitor systemMonitor].cpuUsageInfo;
  686. double h = [self map:cpuUsageInfo.usage FromMin:0.0 FromMax:100.0 ToMin:CPU_COLOR_MIN ToMax:CPU_COLOR_MAX];
  687. #ifdef DEBUG
  688. NSLog(@"CPU Usage: %.3f%%\n", cpuUsageInfo.usage);
  689. #endif
  690. unsigned char r, g, b;
  691. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  692. [self setLightsR:r G:g B:b];
  693. }
  694. - (void)visualizeRAMUsage:(NSTimer *)timer {
  695. JSKMMemoryUsageInfo memoryUsageInfo = [JSKSystemMonitor systemMonitor].memoryUsageInfo;
  696. double h = [self map:memoryUsageInfo.freeMemory FromMin:0.0 FromMax:(memoryUsageInfo.usedMemory + memoryUsageInfo.freeMemory) ToMin:RAM_COLOR_MIN ToMax:RAM_COLOR_MAX];
  697. #ifdef DEBUG
  698. 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);
  699. #endif
  700. unsigned char r, g, b;
  701. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  702. [self setLightsR:r G:g B:b];
  703. }
  704. - (void)visualizeGPUTemperature:(NSTimer *)timer {
  705. JSKSMC *smc = [JSKSMC smc];
  706. double temp = [smc temperatureInCelsiusForKey:KEY_GPU_TEMPERATURE];
  707. if (temp > 1000.0) {
  708. temp /= 1000.0;
  709. }
  710. if (temp > GPU_TEMP_MAX) {
  711. temp = GPU_TEMP_MAX;
  712. }
  713. if (temp < GPU_TEMP_MIN) {
  714. temp = GPU_TEMP_MIN;
  715. }
  716. double h = [self map:temp FromMin:GPU_TEMP_MIN FromMax:GPU_TEMP_MAX ToMin:GPU_COLOR_MIN ToMax:GPU_COLOR_MAX];
  717. #ifdef DEBUG
  718. NSLog(@"GPU Temp %.2f mapped to color %.2f!\n", temp, h);
  719. #endif
  720. unsigned char r, g, b;
  721. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  722. [self setLightsR:r G:g B:b];
  723. }
  724. - (void)visualizeCPUTemperature:(NSTimer *)timer {
  725. JSKSMC *smc = [JSKSMC smc];
  726. double temp = [smc temperatureInCelsiusForKey:KEY_CPU_TEMPERATURE];
  727. if (temp > 1000.0) {
  728. temp /= 1000.0;
  729. }
  730. if (temp > CPU_TEMP_MAX) {
  731. temp = CPU_TEMP_MAX;
  732. }
  733. if (temp < CPU_TEMP_MIN) {
  734. temp = CPU_TEMP_MIN;
  735. }
  736. double h = [self map:temp FromMin:CPU_TEMP_MIN FromMax:CPU_TEMP_MAX ToMin:CPU_COLOR_MIN ToMax:CPU_COLOR_MAX];
  737. #ifdef DEBUG
  738. NSLog(@"CPU Temp %.2f mapped to color %.2f!\n", temp, h);
  739. #endif
  740. unsigned char r, g, b;
  741. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  742. [self setLightsR:r G:g B:b];
  743. }
  744. - (void)visualizeRGBFade:(NSTimer *)timer {
  745. static unsigned char color[3] = { 255, 0, 0 };
  746. static int dec = 0;
  747. static int val = 0;
  748. // Adapted from:
  749. // https://gist.github.com/jamesotron/766994
  750. if (dec < 3) {
  751. int inc = (dec == 2) ? 0 : (dec + 1);
  752. if (val < 255) {
  753. color[dec] -= 1;
  754. color[inc] += 1;
  755. val++;
  756. } else {
  757. val = 0;
  758. dec++;
  759. }
  760. } else {
  761. dec = 0;
  762. }
  763. [self setLightsR:color[0] G:color[1] B:color[2]];
  764. }
  765. - (void)visualizeHSVFade:(NSTimer *)timer {
  766. static float h = 0.0;
  767. if (h < 359.0) {
  768. h += 0.5;
  769. } else {
  770. h = 0.0;
  771. }
  772. unsigned char r, g, b;
  773. [self convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  774. [self setLightsR:r G:g B:b];
  775. }
  776. - (void)visualizeRandom:(NSTimer *)timer {
  777. [self setLightsR:rand() % 256 G:rand() % 256 B:rand() % 256];
  778. }
  779. // -----------------------------------------------------
  780. // --------------------- Utilities ---------------------
  781. // -----------------------------------------------------
  782. - (double)map:(double)val FromMin:(double)fmin FromMax:(double)fmax ToMin:(double)tmin ToMax:(double)tmax {
  783. double norm = (val - fmin) / (fmax - fmin);
  784. return (norm * (tmax - tmin)) + tmin;
  785. }
  786. - (void)convertH:(double)h S:(double)s V:(double)v toR:(unsigned char *)r G:(unsigned char *)g B:(unsigned char *)b {
  787. // Adapted from:
  788. // https://gist.github.com/hdznrrd/656996
  789. if (s == 0.0) {
  790. // Achromatic
  791. *r = *g = *b = (unsigned char)(v * 255);
  792. return;
  793. }
  794. h /= 60; // sector 0 to 5
  795. int i = floor(h);
  796. double f = h - i; // factorial part of h
  797. double p = v * (1 - s);
  798. double q = v * (1 - s * f);
  799. double t = v * (1 - s * (1 - f));
  800. switch (i) {
  801. case 0:
  802. *r = (unsigned char)round(255 * v);
  803. *g = (unsigned char)round(255 * t);
  804. *b = (unsigned char)round(255 * p);
  805. break;
  806. case 1:
  807. *r = (unsigned char)round(255 * q);
  808. *g = (unsigned char)round(255 * v);
  809. *b = (unsigned char)round(255 * p);
  810. break;
  811. case 2:
  812. *r = (unsigned char)round(255 * p);
  813. *g = (unsigned char)round(255 * v);
  814. *b = (unsigned char)round(255 * t);
  815. break;
  816. case 3:
  817. *r = (unsigned char)round(255 * p);
  818. *g = (unsigned char)round(255 * q);
  819. *b = (unsigned char)round(255 * v);
  820. break;
  821. case 4:
  822. *r = (unsigned char)round(255 * t);
  823. *g = (unsigned char)round(255 * p);
  824. *b = (unsigned char)round(255 * v);
  825. break;
  826. default: case 5:
  827. *r = (unsigned char)round(255 * v);
  828. *g = (unsigned char)round(255 * p);
  829. *b = (unsigned char)round(255 * q);
  830. break;
  831. }
  832. }
  833. @end