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 46KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283
  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 "AudioVisualizer.h"
  13. // These are the values stored persistently in the preferences
  14. #define PREF_SERIAL_PORT @"SerialPort"
  15. #define PREF_LIGHTS_STATE @"LightState"
  16. // LED Mode contains the last selected mode as menu item text
  17. #define PREF_LED_MODE @"LEDMode"
  18. #define PREF_BRIGHTNESS @"Brightness"
  19. #define PREF_COLOR @"ManualColor"
  20. #define PREF_SENSITIVITY @"Sensitivity"
  21. #define TEXT_MANUAL @"Select..."
  22. #define TEXT_CPU_USAGE @"CPU Usage"
  23. #define TEXT_RAM_USAGE @"RAM Usage"
  24. #define TEXT_GPU_USAGE @"GPU Usage"
  25. #define TEXT_VRAM_USAGE @"VRAM Usage"
  26. #define TEXT_CPU_TEMPERATURE @"CPU Temperature"
  27. #define TEXT_GPU_TEMPERATURE @"GPU Temperature"
  28. #define TEXT_RGB_FADE @"RGB Fade"
  29. #define TEXT_HSV_FADE @"HSV Fade"
  30. #define TEXT_RANDOM @"Random"
  31. #define TEXT_TEMPLATE_AUDIO @"AudioDevice_%@"
  32. // SMC keys are checked for existence and used for reading
  33. #define KEY_CPU_TEMPERATURE @"TC0D"
  34. #define KEY_GPU_TEMPERATURE @"TG0D"
  35. // Temperature in Celsius
  36. #define CPU_TEMP_MIN 20
  37. #define CPU_TEMP_MAX 90
  38. // HSV Color (S = V = 1)
  39. #define CPU_COLOR_MIN 120
  40. #define CPU_COLOR_MAX 0
  41. #define GPU_TEMP_MIN 20
  42. #define GPU_TEMP_MAX 90
  43. #define GPU_COLOR_MIN 120
  44. #define GPU_COLOR_MAX 0
  45. #define RAM_COLOR_MIN 0
  46. #define RAM_COLOR_MAX 120
  47. // You can play around with these values (skipped pixels, display timer delay) to change CPU usage in display mode
  48. #define AVERAGE_COLOR_PERFORMANCE_INC 10
  49. #define DISPLAY_DELAY 0.1
  50. // Used to identify selected menu items
  51. // displays are all tags >= 0
  52. #define MENU_ITEM_TAG_NOTHING -1
  53. #define MENU_ITEM_TAG_AUDIO -2
  54. // Define this to color the status bar icon according to the current RGB LED value
  55. #define COLORED_MENU_BAR_ICON
  56. @interface AppDelegate ()
  57. @property (weak) IBOutlet NSMenu *statusMenu;
  58. @property (weak) IBOutlet NSMenu *menuColors;
  59. @property (weak) IBOutlet NSMenu *menuAnimations;
  60. @property (weak) IBOutlet NSMenu *menuVisualizations;
  61. @property (weak) IBOutlet NSMenuItem *menuItemDisplays;
  62. @property (weak) IBOutlet NSMenu *menuDisplays;
  63. @property (weak) IBOutlet NSMenuItem *menuItemAudio;
  64. @property (weak) IBOutlet NSMenu *menuAudio;
  65. @property (weak) IBOutlet NSMenu *menuPorts;
  66. @property (weak) IBOutlet NSMenuItem *buttonOff;
  67. @property (weak) IBOutlet NSMenuItem *brightnessItem;
  68. @property (weak) IBOutlet NSSlider *brightnessSlider;
  69. @property (weak) IBOutlet NSMenuItem *brightnessLabel;
  70. @property (weak) IBOutlet NSMenuItem *buttonLights;
  71. @property (weak) IBOutlet NSMenuItem *sensitivityItem;
  72. @property (weak) IBOutlet NSSlider *sensitivitySlider;
  73. @property (weak) IBOutlet NSMenuItem *sensitivityLabel;
  74. @property (weak) IBOutlet NSMenuItem *sensitivityMenu;
  75. @property (strong) NSMenuItem *menuItemColor;
  76. @property (strong) NSStatusItem *statusItem;
  77. @property (strong) NSImage *statusImage;
  78. @property (strong) NSDictionary *staticColors;
  79. @property (strong) NSTimer *animation;
  80. @property (strong) Serial *serial;
  81. @property (strong) NSMenuItem *lastLEDMode;
  82. @end
  83. @implementation AppDelegate
  84. @synthesize statusMenu, application;
  85. @synthesize menuColors, menuAnimations, menuVisualizations, menuPorts;
  86. @synthesize menuItemDisplays, menuDisplays;
  87. @synthesize menuItemAudio, menuAudio;
  88. @synthesize buttonOff, buttonLights;
  89. @synthesize brightnessItem, brightnessSlider, brightnessLabel;
  90. @synthesize sensitivityItem, sensitivitySlider, sensitivityLabel, sensitivityMenu;
  91. @synthesize statusItem, statusImage;
  92. @synthesize staticColors, animation;
  93. @synthesize serial, lastLEDMode, microphone;
  94. @synthesize menuItemColor;
  95. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
  96. srand((unsigned)time(NULL));
  97. [AudioVisualizer setDelegate:self];
  98. serial = [[Serial alloc] init];
  99. lastLEDMode = nil;
  100. animation = nil;
  101. microphone = nil;
  102. // Prepare status bar menu
  103. statusImage = [NSImage imageNamed:@"MenuIcon"];
  104. statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
  105. #ifdef COLORED_MENU_BAR_ICON
  106. [statusImage setTemplate:NO];
  107. [statusItem setImage:[AppDelegate tintedImage:statusImage WithColor:[NSColor colorWithCalibratedRed:0.0f green:0.0f blue:0.0f alpha:1.0f]]];
  108. [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(darkModeChanged:) name:@"AppleInterfaceThemeChangedNotification" object:nil];
  109. #else
  110. [statusImage setTemplate:YES];
  111. [statusItem setImage:statusImage];
  112. #endif
  113. [statusItem setMenu:statusMenu];
  114. // Set default configuration values, load existing ones
  115. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  116. NSMutableDictionary *appDefaults = [NSMutableDictionary dictionaryWithObject:@"" forKey:PREF_SERIAL_PORT];
  117. [appDefaults setObject:[NSNumber numberWithBool:NO] forKey:PREF_LIGHTS_STATE];
  118. [appDefaults setObject:@"" forKey:PREF_LED_MODE];
  119. [appDefaults setObject:[NSNumber numberWithFloat:50.0] forKey:PREF_BRIGHTNESS];
  120. [appDefaults setObject:[NSNumber numberWithFloat:100.0] forKey:PREF_SENSITIVITY];
  121. [store registerDefaults:appDefaults];
  122. [store synchronize];
  123. NSString *savedPort = [store stringForKey:PREF_SERIAL_PORT];
  124. BOOL turnOnLights = [store boolForKey:PREF_LIGHTS_STATE];
  125. NSString *lastMode = [store stringForKey:PREF_LED_MODE];
  126. float brightness = [store floatForKey:PREF_BRIGHTNESS];
  127. NSData *lastColorData = [store dataForKey:PREF_COLOR];
  128. float sensitivity = [store floatForKey:PREF_SENSITIVITY];
  129. NSColor *lastColor = nil;
  130. if (lastColorData != nil) {
  131. lastColor = (NSColor *)[NSUnarchiver unarchiveObjectWithData:lastColorData];
  132. }
  133. // Prepare brightness menu
  134. brightnessItem.view = brightnessSlider;
  135. [brightnessSlider setFloatValue:brightness];
  136. [brightnessLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", brightness]];
  137. // Prepare serial port menu
  138. NSArray *ports = [Serial listSerialPorts];
  139. if ([ports count] > 0) {
  140. [menuPorts removeAllItems];
  141. for (int i = 0; i < [ports count]; i++) {
  142. // Add Menu Item for this port
  143. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
  144. [item setTag:MENU_ITEM_TAG_NOTHING];
  145. [menuPorts addItem:item];
  146. // Set Enabled if it was used the last time
  147. if ((savedPort != nil) && [[ports objectAtIndex:i] isEqualToString:savedPort]) {
  148. [[menuPorts itemAtIndex:i] setState:NSOnState];
  149. // Try to open serial port
  150. [serial setPortName:savedPort];
  151. if ([serial openPort]) {
  152. // Unselect it when an error occured opening the port
  153. [[menuPorts itemAtIndex:i] setState:NSOffState];
  154. }
  155. }
  156. }
  157. }
  158. // Select "Off" button if it was last selected
  159. if ([lastMode isEqualToString:@""]) {
  160. [buttonOff setState:NSOffState];
  161. [self turnLEDsOff:buttonOff];
  162. }
  163. // Prepare static colors menu
  164. staticColors = [NSDictionary dictionaryWithObjectsAndKeys:
  165. [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:0.0f alpha:0.0f], @"Red",
  166. [NSColor colorWithCalibratedRed:0.0f green:1.0f blue:0.0f alpha:0.0f], @"Green",
  167. [NSColor colorWithCalibratedRed:0.0f green:0.0f blue:1.0f alpha:0.0f], @"Blue",
  168. [NSColor colorWithCalibratedRed:0.0f green:1.0f blue:1.0f alpha:0.0f], @"Cyan",
  169. [NSColor colorWithCalibratedRed:1.0f green:0.0f blue:1.0f alpha:0.0f], @"Magenta",
  170. [NSColor colorWithCalibratedRed:1.0f green:1.0f blue:0.0f alpha:0.0f], @"Yellow",
  171. [NSColor colorWithCalibratedRed:1.0f green:1.0f blue:1.0f alpha:0.0f], @"White",
  172. nil];
  173. for (NSString *key in [staticColors allKeys]) {
  174. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
  175. [item setTag:MENU_ITEM_TAG_NOTHING];
  176. if ([key isEqualToString:lastMode]) {
  177. [self selectedVisualization:item];
  178. }
  179. [menuColors addItem:item];
  180. }
  181. menuItemColor = [[NSMenuItem alloc] initWithTitle:TEXT_MANUAL action:@selector(setColorSelected:) keyEquivalent:@""];
  182. if ([lastMode isEqualToString:TEXT_MANUAL]) {
  183. if (lastColor != nil) {
  184. // Restore previously set RGB color
  185. [self setLightsColor:lastColor];
  186. }
  187. [menuItemColor setState:NSOnState];
  188. }
  189. [menuItemColor setTag:MENU_ITEM_TAG_NOTHING];
  190. [menuColors addItem:menuItemColor];
  191. // Prepare animations menu
  192. NSArray *animationStrings = [NSArray arrayWithObjects:
  193. TEXT_RGB_FADE,
  194. TEXT_HSV_FADE,
  195. TEXT_RANDOM,
  196. nil];
  197. for (NSString *key in animationStrings) {
  198. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:key action:@selector(selectedVisualization:) keyEquivalent:@""];
  199. [item setTag:MENU_ITEM_TAG_NOTHING];
  200. if ([key isEqualToString:lastMode]) {
  201. [self selectedVisualization:item];
  202. }
  203. [menuAnimations addItem:item];
  204. }
  205. // Add CPU Usage menu item
  206. NSMenuItem *cpuUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  207. [cpuUsageItem setTag:MENU_ITEM_TAG_NOTHING];
  208. if ([lastMode isEqualToString:TEXT_CPU_USAGE]) {
  209. [self selectedVisualization:cpuUsageItem];
  210. }
  211. [menuVisualizations addItem:cpuUsageItem];
  212. // Add Memory Usage item
  213. NSMenuItem *memoryUsageItem = [[NSMenuItem alloc] initWithTitle:TEXT_RAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  214. [memoryUsageItem setTag:MENU_ITEM_TAG_NOTHING];
  215. if ([lastMode isEqualToString:TEXT_RAM_USAGE]) {
  216. [self selectedVisualization:memoryUsageItem];
  217. }
  218. [menuVisualizations addItem:memoryUsageItem];
  219. // Check if GPU Stats are available, add menu items if so
  220. NSNumber *usage;
  221. NSNumber *freeVRAM;
  222. NSNumber *usedVRAM;
  223. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  224. NSLog(@"Error reading GPU information\n");
  225. } else {
  226. NSMenuItem *itemUsage = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  227. [itemUsage setTag:MENU_ITEM_TAG_NOTHING];
  228. if ([lastMode isEqualToString:TEXT_GPU_USAGE]) {
  229. [self selectedVisualization:itemUsage];
  230. }
  231. [menuVisualizations addItem:itemUsage];
  232. NSMenuItem *itemVRAM = [[NSMenuItem alloc] initWithTitle:TEXT_VRAM_USAGE action:@selector(selectedVisualization:) keyEquivalent:@""];
  233. [itemVRAM setTag:MENU_ITEM_TAG_NOTHING];
  234. if ([lastMode isEqualToString:TEXT_VRAM_USAGE]) {
  235. [self selectedVisualization:itemVRAM];
  236. }
  237. [menuVisualizations addItem:itemVRAM];
  238. }
  239. // Check available temperatures and add menu items
  240. JSKSMC *smc = [JSKSMC smc];
  241. for (int i = 0; i < [[smc workingTempKeys] count]; i++) {
  242. NSString *key = [smc.workingTempKeys objectAtIndex:i];
  243. #ifdef DEBUG
  244. NSString *name = [smc humanReadableNameForKey:key];
  245. NSLog(@"Sensor \"%@\": \"%@\"\n", key, name);
  246. #endif
  247. if ([key isEqualToString:KEY_CPU_TEMPERATURE]) {
  248. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_CPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
  249. [item setTag:MENU_ITEM_TAG_NOTHING];
  250. if ([lastMode isEqualToString:TEXT_CPU_TEMPERATURE]) {
  251. [self selectedVisualization:item];
  252. }
  253. [menuVisualizations addItem:item];
  254. }
  255. if ([key isEqualToString:KEY_GPU_TEMPERATURE]) {
  256. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:TEXT_GPU_TEMPERATURE action:@selector(selectedVisualization:) keyEquivalent:@""];
  257. [item setTag:MENU_ITEM_TAG_NOTHING];
  258. if ([lastMode isEqualToString:TEXT_GPU_TEMPERATURE]) {
  259. [self selectedVisualization:item];
  260. }
  261. [menuVisualizations addItem:item];
  262. }
  263. }
  264. // Restore previously used lights configuration
  265. if (turnOnLights) {
  266. // Turn on lights
  267. if ([serial isOpen]) {
  268. [serial sendString:@"UV 1\n"];
  269. }
  270. [buttonLights setState:NSOnState];
  271. } else {
  272. // Turn off lights
  273. if ([serial isOpen]) {
  274. [serial sendString:@"UV 0\n"];
  275. }
  276. }
  277. // List available displays and add menu items
  278. [Screenshot init:self];
  279. NSArray *displayIDs = [Screenshot listDisplays];
  280. [self updateDisplayUI:displayIDs];
  281. // List available audio input devices and add menu items
  282. NSArray *inputDevices = [EZAudioDevice inputDevices];
  283. [menuAudio removeAllItems];
  284. for (int i = 0; i < [inputDevices count]; i++) {
  285. EZAudioDevice *dev = [inputDevices objectAtIndex:i];
  286. #ifdef DEBUG
  287. NSLog(@"Audio input device: \"%@\"\n", [dev name]);
  288. #endif
  289. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[dev name] action:@selector(selectedVisualization:) keyEquivalent:@""];
  290. [item setTag:MENU_ITEM_TAG_AUDIO];
  291. NSString *lastModeString = [NSString stringWithFormat:TEXT_TEMPLATE_AUDIO, [dev name]];
  292. if ([lastModeString isEqualToString:lastMode]) {
  293. [self selectedVisualization:item];
  294. }
  295. [menuAudio addItem:item];
  296. }
  297. if ([inputDevices count] > 0) {
  298. [menuItemAudio setHidden:NO];
  299. // Prepare sensitivity menu
  300. sensitivityItem.view = sensitivitySlider;
  301. [sensitivitySlider setFloatValue:sensitivity];
  302. [sensitivityLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", sensitivity]];
  303. [sensitivityMenu setHidden:NO];
  304. [AudioVisualizer setSensitivity:sensitivity];
  305. }
  306. }
  307. - (void)applicationWillTerminate:(NSNotification *)aNotification {
  308. // Stop previous timer setting
  309. if (animation != nil) {
  310. [animation invalidate];
  311. animation = nil;
  312. }
  313. // Stop previous audio data retrieval
  314. if (microphone != nil) {
  315. [microphone setMicrophoneOn:NO];
  316. }
  317. // Remove display callback
  318. [Screenshot close:self];
  319. // Turn off all lights if possible
  320. if ([serial isOpen]) {
  321. [serial sendString:@"RGB 0 0 0\n"];
  322. [serial sendString:@"UV 0\n"];
  323. [serial closePort];
  324. }
  325. }
  326. - (void)clearDisplayUI {
  327. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  328. if ([[menuDisplays itemAtIndex:i] isEnabled] == YES) {
  329. // A display configuration is currently selected. Disable the timer
  330. if (animation != nil) {
  331. [animation invalidate];
  332. animation = nil;
  333. }
  334. }
  335. }
  336. [menuDisplays removeAllItems];
  337. [menuItemDisplays setHidden:YES];
  338. }
  339. - (void)updateDisplayUI:(NSArray *)displayIDs {
  340. if ([displayIDs count] > 0) {
  341. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  342. NSString *lastMode = [store stringForKey:PREF_LED_MODE];
  343. [menuItemDisplays setHidden:NO];
  344. for (int i = 0; i < [displayIDs count]; i++) {
  345. NSString *title = [Screenshot displayNameFromDisplayID:[displayIDs objectAtIndex:i]];
  346. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title
  347. action:@selector(selectedVisualization:)
  348. keyEquivalent:@""];
  349. [item setTag:[[displayIDs objectAtIndex:i] integerValue]];
  350. if ([title isEqualToString:lastMode]) {
  351. [self selectedVisualization:item];
  352. }
  353. [menuDisplays addItem:item];
  354. }
  355. }
  356. }
  357. - (void)setLightsColor:(NSColor *)color {
  358. CGFloat red, green, blue, alpha;
  359. [color getRed:&red green:&green blue:&blue alpha:&alpha];
  360. [self setLightsR:red * 255 G:green * 255 B:blue * 255];
  361. // Stop previous timer setting
  362. if (animation != nil) {
  363. [animation invalidate];
  364. animation = nil;
  365. }
  366. // Stop previous audio data retrieval
  367. if (microphone != nil) {
  368. [microphone setMicrophoneOn:NO];
  369. }
  370. // Turn off all other LED menu items
  371. if (menuColors != nil) {
  372. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  373. [[menuColors itemAtIndex:i] setState:NSOffState];
  374. }
  375. }
  376. if (menuAnimations != nil) {
  377. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  378. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  379. }
  380. }
  381. if (menuVisualizations != nil) {
  382. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  383. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  384. }
  385. }
  386. if (menuAudio != nil) {
  387. for (int i = 0; i < [menuAudio numberOfItems]; i++) {
  388. [[menuAudio itemAtIndex:i] setState:NSOffState];
  389. }
  390. }
  391. if (menuDisplays != nil) {
  392. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  393. [[menuDisplays itemAtIndex:i] setState:NSOffState];
  394. }
  395. }
  396. [buttonOff setState:NSOffState];
  397. [menuItemColor setState:NSOnState];
  398. // Store new manually selected color
  399. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  400. NSData *data = [NSArchiver archivedDataWithRootObject:color];
  401. [store setObject:data forKey:PREF_COLOR];
  402. [store setObject:TEXT_MANUAL forKey:PREF_LED_MODE];
  403. [store synchronize];
  404. }
  405. - (void)setLightsR:(unsigned char)r G:(unsigned char)g B:(unsigned char)b {
  406. if ([serial isOpen]) {
  407. unsigned char red = r * ([brightnessSlider floatValue] / 100.0);
  408. unsigned char green = g * ([brightnessSlider floatValue] / 100.0);
  409. unsigned char blue = b * ([brightnessSlider floatValue] / 100.0);
  410. [serial sendString:[NSString stringWithFormat:@"RGB %d %d %d\n", red, green, blue]];
  411. } else {
  412. #ifdef DEBUG
  413. NSLog(@"Trying to send RGB without opened port!\n");
  414. #endif
  415. }
  416. #ifdef COLORED_MENU_BAR_ICON
  417. [statusItem setImage:[AppDelegate tintedImage:statusImage WithColor:[NSColor colorWithCalibratedRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:1.0f]]];
  418. #endif
  419. }
  420. - (IBAction)relistSerialPorts:(id)sender {
  421. // Refill audio device list
  422. NSArray *inputDevices = [EZAudioDevice inputDevices];
  423. [menuAudio removeAllItems];
  424. for (int i = 0; i < [inputDevices count]; i++) {
  425. EZAudioDevice *dev = [inputDevices objectAtIndex:i];
  426. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[dev name] action:@selector(selectedVisualization:) keyEquivalent:@""];
  427. [item setTag:MENU_ITEM_TAG_AUDIO];
  428. NSString *lastModeString = [NSString stringWithFormat:TEXT_TEMPLATE_AUDIO, [dev name]];
  429. if ([lastModeString isEqualToString:[[NSUserDefaults standardUserDefaults] stringForKey:PREF_LED_MODE]]) {
  430. [self selectedVisualization:item];
  431. }
  432. [menuAudio addItem:item];
  433. }
  434. if ([inputDevices count] > 0) {
  435. [menuItemAudio setHidden:NO];
  436. } else {
  437. [menuItemAudio setHidden:YES];
  438. }
  439. // Refill port list
  440. NSArray *ports = [Serial listSerialPorts];
  441. [menuPorts removeAllItems];
  442. for (int i = 0; i < [ports count]; i++) {
  443. // Add Menu Item for this port
  444. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
  445. [item setTag:MENU_ITEM_TAG_NOTHING];
  446. [menuPorts addItem:item];
  447. // Mark it if it is currently open
  448. if ([serial isOpen]) {
  449. if ([[ports objectAtIndex:i] isEqualToString:[serial portName]]) {
  450. [[menuPorts itemAtIndex:i] setState:NSOnState];
  451. }
  452. }
  453. }
  454. }
  455. - (void)setColorSelected:(NSMenuItem *)sender {
  456. NSColorPanel *cp = [NSColorPanel sharedColorPanel];
  457. [cp setTarget:self];
  458. [cp setAction:@selector(colorSelected:)];
  459. [cp setShowsAlpha:NO];
  460. [cp setContinuous:NO];
  461. [cp setMode:NSRGBModeColorPanel];
  462. // Try to restore last manually selected color
  463. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  464. NSData *lastColorData = [store dataForKey:PREF_COLOR];
  465. NSColor *lastColor = nil;
  466. if (lastColorData != nil) {
  467. lastColor = (NSColor *)[NSUnarchiver unarchiveObjectWithData:lastColorData];
  468. [cp setColor:lastColor];
  469. }
  470. [NSApp activateIgnoringOtherApps:YES];
  471. [application orderFrontColorPanel:cp];
  472. }
  473. - (void)colorSelected:(NSColorPanel *)sender {
  474. [self setLightsColor:[sender color]];
  475. }
  476. - (IBAction)sensitivityMoved:(NSSlider *)sender {
  477. [sensitivityLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", [sender floatValue]]];
  478. [AudioVisualizer setSensitivity:[sender floatValue]];
  479. // Store changed value in preferences
  480. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  481. [store setObject:[NSNumber numberWithFloat:[sender floatValue]] forKey:PREF_SENSITIVITY];
  482. [store synchronize];
  483. }
  484. - (IBAction)brightnessMoved:(NSSlider *)sender {
  485. [brightnessLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", [sender floatValue]]];
  486. // Restore the current configuration for items where it won't happen automatically
  487. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  488. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  489. [self selectedVisualization:[menuColors itemAtIndex:i]];
  490. }
  491. }
  492. // Store changed value in preferences
  493. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  494. [store setObject:[NSNumber numberWithFloat:[sender floatValue]] forKey:PREF_BRIGHTNESS];
  495. [store synchronize];
  496. }
  497. - (IBAction)turnLEDsOff:(NSMenuItem *)sender {
  498. if ([sender state] == NSOffState) {
  499. lastLEDMode = nil;
  500. // Stop previous timer setting
  501. if (animation != nil) {
  502. [animation invalidate];
  503. animation = nil;
  504. }
  505. // Stop previous audio data retrieval
  506. if (microphone != nil) {
  507. [microphone setMicrophoneOn:NO];
  508. }
  509. // Turn off all other LED menu items
  510. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  511. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  512. lastLEDMode = [menuColors itemAtIndex:i];
  513. }
  514. [[menuColors itemAtIndex:i] setState:NSOffState];
  515. }
  516. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  517. if ([[menuAnimations itemAtIndex:i] state] == NSOnState) {
  518. lastLEDMode = [menuAnimations itemAtIndex:i];
  519. }
  520. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  521. }
  522. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  523. if ([[menuVisualizations itemAtIndex:i] state] == NSOnState) {
  524. lastLEDMode = [menuVisualizations itemAtIndex:i];
  525. }
  526. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  527. }
  528. for (int i = 0; i < [menuAudio numberOfItems]; i++) {
  529. if ([[menuAudio itemAtIndex:i] state] == NSOnState) {
  530. lastLEDMode = [menuAudio itemAtIndex:i];
  531. }
  532. [[menuAudio itemAtIndex:i] setState:NSOffState];
  533. }
  534. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  535. if ([[menuDisplays itemAtIndex:i] state] == NSOnState) {
  536. lastLEDMode = [menuDisplays itemAtIndex:i];
  537. }
  538. [[menuDisplays itemAtIndex:i] setState:NSOffState];
  539. }
  540. // Turn on "off" menu item
  541. [sender setState:NSOnState];
  542. // Store changed value in preferences
  543. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  544. [store setObject:@"" forKey:PREF_LED_MODE];
  545. [store synchronize];
  546. #ifdef DEBUG
  547. NSLog(@"Stored new mode: \"off\"!\n");
  548. #endif
  549. // Send command to turn off LEDs
  550. [self setLightsR:0 G:0 B:0];
  551. } else {
  552. // Try to restore last LED setting
  553. if (lastLEDMode != nil) {
  554. [self selectedVisualization:lastLEDMode];
  555. }
  556. }
  557. }
  558. - (IBAction)toggleLights:(NSMenuItem *)sender {
  559. if ([sender state] == NSOffState) {
  560. // Turn on lights
  561. if ([serial isOpen]) {
  562. [serial sendString:@"UV 1\n"];
  563. }
  564. [sender setState:NSOnState];
  565. } else {
  566. // Turn off lights
  567. if ([serial isOpen]) {
  568. [serial sendString:@"UV 0\n"];
  569. }
  570. [sender setState:NSOffState];
  571. }
  572. // Store changed value in preferences
  573. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  574. [store setBool:([sender state] == NSOnState) forKey:PREF_LIGHTS_STATE];
  575. [store synchronize];
  576. }
  577. - (BOOL)timedVisualization:(NSString *)mode {
  578. // Stop previous timer setting
  579. if (animation != nil) {
  580. [animation invalidate];
  581. animation = nil;
  582. }
  583. // Stop previous audio data retrieval
  584. if (microphone != nil) {
  585. [microphone setMicrophoneOn:NO];
  586. }
  587. // Schedule next invocation for this animation...
  588. if ([mode isEqualToString:TEXT_GPU_USAGE]) {
  589. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeGPUUsage:) userInfo:mode repeats:YES];
  590. } else if ([mode isEqualToString:TEXT_VRAM_USAGE]) {
  591. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeVRAMUsage:) userInfo:mode repeats:YES];
  592. } else if ([mode isEqualToString:TEXT_CPU_USAGE]) {
  593. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeCPUUsage:) userInfo:mode repeats:YES];
  594. } else if ([mode isEqualToString:TEXT_RAM_USAGE]) {
  595. animation = [NSTimer scheduledTimerWithTimeInterval:20.0 target:self selector:@selector(visualizeRAMUsage:) userInfo:mode repeats:YES];
  596. } else if ([mode isEqualToString:TEXT_CPU_TEMPERATURE]) {
  597. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeCPUTemperature:) userInfo:mode repeats:YES];
  598. } else if ([mode isEqualToString:TEXT_GPU_TEMPERATURE]) {
  599. animation = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(visualizeGPUTemperature:) userInfo:mode repeats:YES];
  600. } else if ([mode isEqualToString:TEXT_RGB_FADE]) {
  601. animation = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(visualizeRGBFade:) userInfo:mode repeats:YES];
  602. } else if ([mode isEqualToString:TEXT_HSV_FADE]) {
  603. animation = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(visualizeHSVFade:) userInfo:mode repeats:YES];
  604. } else if ([mode isEqualToString:TEXT_RANDOM]) {
  605. animation = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(visualizeRandom:) userInfo:mode repeats:YES];
  606. } else {
  607. return NO;
  608. }
  609. #ifdef DEBUG
  610. NSLog(@"Scheduled animation for \"%@\"!\n", mode);
  611. #endif
  612. // ...and also execute it right now
  613. [animation fire];
  614. return YES;
  615. }
  616. - (void)displayVisualization:(NSMenuItem *)sender {
  617. // Stop previous timer setting
  618. if (animation != nil) {
  619. [animation invalidate];
  620. animation = nil;
  621. }
  622. // Stop previous audio data retrieval
  623. if (microphone != nil) {
  624. [microphone setMicrophoneOn:NO];
  625. }
  626. // Schedule next invocation for this animation...
  627. animation = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:[NSNumber numberWithInteger:[sender tag]] repeats:YES];
  628. // ...and also execute it right now
  629. [animation fire];
  630. }
  631. - (void)selectedVisualization:(NSMenuItem *)sender {
  632. // Turn off all other LED menu items
  633. if (menuColors != nil) {
  634. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  635. [[menuColors itemAtIndex:i] setState:NSOffState];
  636. }
  637. }
  638. if (menuAnimations != nil) {
  639. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  640. [[menuAnimations itemAtIndex:i] setState:NSOffState];
  641. }
  642. }
  643. if (menuVisualizations != nil) {
  644. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  645. [[menuVisualizations itemAtIndex:i] setState:NSOffState];
  646. }
  647. }
  648. if (menuAudio != nil) {
  649. for (int i = 0; i < [menuAudio numberOfItems]; i++) {
  650. [[menuAudio itemAtIndex:i] setState:NSOffState];
  651. }
  652. }
  653. if (menuDisplays != nil) {
  654. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  655. [[menuDisplays itemAtIndex:i] setState:NSOffState];
  656. }
  657. }
  658. [buttonOff setState:NSOffState];
  659. [sender setState:NSOnState];
  660. // Check if it is a display
  661. BOOL found = NO;
  662. if ([sender tag] > MENU_ITEM_TAG_NOTHING) {
  663. found = YES;
  664. [self displayVisualization:sender];
  665. }
  666. // Check if it is an audio input device
  667. if ((found == NO) && ([sender tag] == MENU_ITEM_TAG_AUDIO)) {
  668. // Stop previous timer setting
  669. if (animation != nil) {
  670. [animation invalidate];
  671. animation = nil;
  672. }
  673. found = YES;
  674. BOOL foundDev = NO;
  675. NSArray *audioDevices = [EZAudioDevice inputDevices];
  676. for (int i = 0; i < [audioDevices count]; i++) {
  677. EZAudioDevice *dev = [audioDevices objectAtIndex:i];
  678. if ([[dev name] isEqualToString:[sender title]]) {
  679. // Send command to turn off LEDs
  680. [self setLightsR:0 G:0 B:0];
  681. // Found device
  682. foundDev = YES;
  683. if (microphone != nil) {
  684. [microphone setMicrophoneOn:NO];
  685. } else {
  686. microphone = [EZMicrophone microphoneWithDelegate:self];
  687. }
  688. [microphone setDevice:dev];
  689. [microphone setMicrophoneOn:YES];
  690. break;
  691. }
  692. }
  693. if (foundDev == NO) {
  694. NSLog(@"Couldn't find device \"%@\"\n", [sender title]);
  695. [sender setState:NSOffState];
  696. // List available audio input devices and add menu items
  697. NSArray *inputDevices = [EZAudioDevice inputDevices];
  698. [menuAudio removeAllItems];
  699. for (int i = 0; i < [inputDevices count]; i++) {
  700. EZAudioDevice *dev = [inputDevices objectAtIndex:i];
  701. NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[dev name] action:@selector(selectedVisualization:) keyEquivalent:@""];
  702. [item setTag:MENU_ITEM_TAG_AUDIO];
  703. if ([[dev name] isEqualToString:[sender title]]) {
  704. // Found the device the user really wanted
  705. [self selectedVisualization:item];
  706. }
  707. [menuAudio addItem:item];
  708. }
  709. return; // Don't store new mode
  710. }
  711. }
  712. // Check if it is the manual color select item
  713. if ((found == NO) && ([sender.title isEqualToString:TEXT_MANUAL])) {
  714. found = YES;
  715. [self colorSelected:[NSColorPanel sharedColorPanel]];
  716. }
  717. // Check if a static color was selected
  718. if ((found == NO) && (staticColors != nil)) {
  719. for (NSString *key in [staticColors allKeys]) {
  720. if ([sender.title isEqualToString:key]) {
  721. found = YES;
  722. // Stop previous timer setting
  723. if (animation != nil) {
  724. [animation invalidate];
  725. animation = nil;
  726. }
  727. // Stop previous audio data retrieval
  728. if (microphone != nil) {
  729. [microphone setMicrophoneOn:NO];
  730. }
  731. NSColor *color = [staticColors valueForKey:key];
  732. unsigned char red = [color redComponent] * 255;
  733. unsigned char green = [color greenComponent] * 255;
  734. unsigned char blue = [color blueComponent] * 255;
  735. [self setLightsR:red G:green B:blue];
  736. break;
  737. }
  738. }
  739. }
  740. if (found == NO) {
  741. // Check if an animated visualization was selected
  742. if ([self timedVisualization:[sender title]] == NO) {
  743. NSLog(@"Unknown LED Visualization selected!\n");
  744. return;
  745. }
  746. }
  747. // Store changed value in preferences
  748. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  749. if ([sender tag] == MENU_ITEM_TAG_AUDIO) {
  750. // Prepend text for audio device names
  751. NSString *tmp = [NSString stringWithFormat:TEXT_TEMPLATE_AUDIO, [sender title]];
  752. [store setObject:tmp forKey:PREF_LED_MODE];
  753. } else {
  754. [store setObject:[sender title] forKey:PREF_LED_MODE];
  755. }
  756. [store synchronize];
  757. #ifdef DEBUG
  758. NSLog(@"Stored new mode: \"%@\"!\n", [sender title]);
  759. #endif
  760. }
  761. - (void)selectedSerialPort:(NSMenuItem *)source {
  762. // Store selection for next start-up
  763. NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
  764. [store setObject:[source title] forKey:PREF_SERIAL_PORT];
  765. [store synchronize];
  766. // De-select all other ports
  767. for (int i = 0; i < [menuPorts numberOfItems]; i++) {
  768. [[menuPorts itemAtIndex:i] setState:NSOffState];
  769. }
  770. // Select only the current port
  771. [source setState:NSOnState];
  772. // Close previously opened port, if any
  773. if ([serial isOpen]) {
  774. [serial closePort];
  775. }
  776. // Try to open selected port
  777. [serial setPortName:[source title]];
  778. if ([serial openPort] != 0) {
  779. [source setState:NSOffState];
  780. } else {
  781. // Restore the current configuration
  782. for (int i = 0; i < [menuColors numberOfItems]; i++) {
  783. if ([[menuColors itemAtIndex:i] state] == NSOnState) {
  784. [self selectedVisualization:[menuColors itemAtIndex:i]];
  785. }
  786. }
  787. for (int i = 0; i < [menuAnimations numberOfItems]; i++) {
  788. if ([[menuAnimations itemAtIndex:i] state] == NSOnState) {
  789. [self selectedVisualization:[menuAnimations itemAtIndex:i]];
  790. }
  791. }
  792. for (int i = 0; i < [menuVisualizations numberOfItems]; i++) {
  793. if ([[menuVisualizations itemAtIndex:i] state] == NSOnState) {
  794. [self selectedVisualization:[menuVisualizations itemAtIndex:i]];
  795. }
  796. }
  797. for (int i = 0; i < [menuAudio numberOfItems]; i++) {
  798. if ([[menuAudio itemAtIndex:i] state] == NSOnState) {
  799. [self selectedVisualization:[menuAudio itemAtIndex:i]];
  800. }
  801. }
  802. for (int i = 0; i < [menuDisplays numberOfItems]; i++) {
  803. if ([[menuDisplays itemAtIndex:i] state] == NSOnState) {
  804. [self selectedVisualization:[menuDisplays itemAtIndex:i]];
  805. }
  806. }
  807. if ([buttonOff state] == NSOnState) {
  808. [buttonOff setState:NSOffState];
  809. [self turnLEDsOff:buttonOff];
  810. }
  811. if ([buttonLights state] == NSOnState) {
  812. [serial sendString:@"UV 1\n"];
  813. } else {
  814. [serial sendString:@"UV 0\n"];
  815. }
  816. }
  817. }
  818. - (IBAction)showAbout:(id)sender {
  819. [NSApp activateIgnoringOtherApps:YES];
  820. [application orderFrontStandardAboutPanel:self];
  821. }
  822. #ifdef COLORED_MENU_BAR_ICON
  823. - (void)darkModeChanged:(NSNotification *)notification {
  824. [statusItem setImage:[AppDelegate tintedImage:statusImage WithColor:nil]];
  825. }
  826. #endif
  827. // ------------------------------------------------------
  828. // ----------------- Microphone Delegate ----------------
  829. // ------------------------------------------------------
  830. - (void)microphone:(EZMicrophone *)microphone hasAudioReceived:(float **)buffer withBufferSize:(UInt32)bufferSize withNumberOfChannels:(UInt32)numberOfChannels {
  831. __weak typeof (self) weakSelf = self;
  832. // Getting audio data as an array of float buffer arrays that can be fed into the
  833. // EZAudioPlot, EZAudioPlotGL, or whatever visualization you would like to do with
  834. // the microphone data.
  835. dispatch_async(dispatch_get_main_queue(),^{
  836. if (weakSelf.microphone.microphoneOn == NO) {
  837. return;
  838. }
  839. // buffer[0] = left channel, buffer[1] = right channel
  840. [AudioVisualizer updateBuffer:buffer[0] withBufferSize:bufferSize];
  841. });
  842. }
  843. - (void)microphone:(EZMicrophone *)microphone changedDevice:(EZAudioDevice *)device {
  844. dispatch_async(dispatch_get_main_queue(), ^{
  845. NSLog(@"Changed audio input device: %@", [device name]);
  846. });
  847. }
  848. // ------------------------------------------------------
  849. // ------------------- Visualizations -------------------
  850. // ------------------------------------------------------
  851. - (void)visualizeDisplay:(NSTimer *)timer {
  852. NSBitmapImageRep *screen = [Screenshot screenshot:[timer userInfo]];
  853. NSInteger spp = [screen samplesPerPixel];
  854. if (((spp != 3) && (spp != 4)) || ([screen isPlanar] == YES) || ([screen numberOfPlanes] != 1)) {
  855. NSLog(@"Unknown image format (%ld, %c, %ld)!\n", (long)spp, ([screen isPlanar] == YES) ? 'p' : 'n', (long)[screen numberOfPlanes]);
  856. return;
  857. }
  858. int redC = 0, greenC = 1, blueC = 2;
  859. if ([screen bitmapFormat] & NSAlphaFirstBitmapFormat) {
  860. redC = 1; greenC = 2; blueC = 3;
  861. }
  862. unsigned char *data = [screen bitmapData];
  863. unsigned long width = [screen pixelsWide];
  864. unsigned long height = [screen pixelsHigh];
  865. unsigned long max = width * height;
  866. unsigned long red = 0, green = 0, blue = 0;
  867. for (unsigned long i = 0; i < max; i += AVERAGE_COLOR_PERFORMANCE_INC) {
  868. unsigned long off = spp * i;
  869. red += data[off + redC];
  870. green += data[off + greenC];
  871. blue += data[off + blueC];
  872. }
  873. max /= AVERAGE_COLOR_PERFORMANCE_INC;
  874. [self setLightsR:(red / max) G:(green / max) B:(blue / max)];
  875. }
  876. - (void)visualizeGPUUsage:(NSTimer *)timer {
  877. NSNumber *usage;
  878. NSNumber *freeVRAM;
  879. NSNumber *usedVRAM;
  880. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  881. NSLog(@"Error reading GPU information\n");
  882. } else {
  883. double h = [AppDelegate map:[usage doubleValue] FromMin:0.0 FromMax:100.0 ToMin:GPU_COLOR_MIN ToMax:GPU_COLOR_MAX];
  884. #ifdef DEBUG
  885. NSLog(@"GPU Usage: %.3f%%\n", [usage doubleValue]);
  886. #endif
  887. unsigned char r, g, b;
  888. [AppDelegate convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  889. [self setLightsR:r G:g B:b];
  890. }
  891. }
  892. - (void)visualizeVRAMUsage:(NSTimer *)timer {
  893. NSNumber *usage;
  894. NSNumber *freeVRAM;
  895. NSNumber *usedVRAM;
  896. if ([GPUStats getGPUUsage:&usage freeVRAM:&freeVRAM usedVRAM:&usedVRAM] != 0) {
  897. NSLog(@"Error reading GPU information\n");
  898. } else {
  899. double h = [AppDelegate map:[freeVRAM doubleValue] FromMin:0.0 FromMax:([freeVRAM doubleValue] + [usedVRAM doubleValue]) ToMin:RAM_COLOR_MIN ToMax:RAM_COLOR_MAX];
  900. #ifdef DEBUG
  901. 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);
  902. #endif
  903. unsigned char r, g, b;
  904. [AppDelegate convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  905. [self setLightsR:r G:g B:b];
  906. }
  907. }
  908. - (void)visualizeCPUUsage:(NSTimer *)timer {
  909. JSKMCPUUsageInfo cpuUsageInfo = [JSKSystemMonitor systemMonitor].cpuUsageInfo;
  910. double h = [AppDelegate map:cpuUsageInfo.usage FromMin:0.0 FromMax:100.0 ToMin:CPU_COLOR_MIN ToMax:CPU_COLOR_MAX];
  911. #ifdef DEBUG
  912. NSLog(@"CPU Usage: %.3f%%\n", cpuUsageInfo.usage);
  913. #endif
  914. unsigned char r, g, b;
  915. [AppDelegate convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  916. [self setLightsR:r G:g B:b];
  917. }
  918. - (void)visualizeRAMUsage:(NSTimer *)timer {
  919. JSKMMemoryUsageInfo memoryUsageInfo = [JSKSystemMonitor systemMonitor].memoryUsageInfo;
  920. double h = [AppDelegate map:memoryUsageInfo.freeMemory FromMin:0.0 FromMax:(memoryUsageInfo.usedMemory + memoryUsageInfo.freeMemory) ToMin:RAM_COLOR_MIN ToMax:RAM_COLOR_MAX];
  921. #ifdef DEBUG
  922. 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);
  923. #endif
  924. unsigned char r, g, b;
  925. [AppDelegate convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  926. [self setLightsR:r G:g B:b];
  927. }
  928. - (void)visualizeGPUTemperature:(NSTimer *)timer {
  929. JSKSMC *smc = [JSKSMC smc];
  930. double temp = [smc temperatureInCelsiusForKey:KEY_GPU_TEMPERATURE];
  931. if (temp > 1000.0) {
  932. temp /= 1000.0;
  933. }
  934. if (temp > GPU_TEMP_MAX) {
  935. temp = GPU_TEMP_MAX;
  936. }
  937. if (temp < GPU_TEMP_MIN) {
  938. temp = GPU_TEMP_MIN;
  939. }
  940. double h = [AppDelegate map:temp FromMin:GPU_TEMP_MIN FromMax:GPU_TEMP_MAX ToMin:GPU_COLOR_MIN ToMax:GPU_COLOR_MAX];
  941. #ifdef DEBUG
  942. NSLog(@"GPU Temp %.2f mapped to color %.2f!\n", temp, h);
  943. #endif
  944. unsigned char r, g, b;
  945. [AppDelegate convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  946. [self setLightsR:r G:g B:b];
  947. }
  948. - (void)visualizeCPUTemperature:(NSTimer *)timer {
  949. JSKSMC *smc = [JSKSMC smc];
  950. double temp = [smc temperatureInCelsiusForKey:KEY_CPU_TEMPERATURE];
  951. if (temp > 1000.0) {
  952. temp /= 1000.0;
  953. }
  954. if (temp > CPU_TEMP_MAX) {
  955. temp = CPU_TEMP_MAX;
  956. }
  957. if (temp < CPU_TEMP_MIN) {
  958. temp = CPU_TEMP_MIN;
  959. }
  960. double h = [AppDelegate map:temp FromMin:CPU_TEMP_MIN FromMax:CPU_TEMP_MAX ToMin:CPU_COLOR_MIN ToMax:CPU_COLOR_MAX];
  961. #ifdef DEBUG
  962. NSLog(@"CPU Temp %.2f mapped to color %.2f!\n", temp, h);
  963. #endif
  964. unsigned char r, g, b;
  965. [AppDelegate convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  966. [self setLightsR:r G:g B:b];
  967. }
  968. - (void)visualizeRGBFade:(NSTimer *)timer {
  969. static unsigned char color[3] = { 255, 0, 0 };
  970. static int dec = 0;
  971. static int val = 0;
  972. // Adapted from:
  973. // https://gist.github.com/jamesotron/766994
  974. if (dec < 3) {
  975. int inc = (dec == 2) ? 0 : (dec + 1);
  976. if (val < 255) {
  977. color[dec] -= 1;
  978. color[inc] += 1;
  979. val++;
  980. } else {
  981. val = 0;
  982. dec++;
  983. }
  984. } else {
  985. dec = 0;
  986. }
  987. [self setLightsR:color[0] G:color[1] B:color[2]];
  988. }
  989. - (void)visualizeHSVFade:(NSTimer *)timer {
  990. static float h = 0.0;
  991. if (h < 359.0) {
  992. h += 0.5;
  993. } else {
  994. h = 0.0;
  995. }
  996. unsigned char r, g, b;
  997. [AppDelegate convertH:h S:1.0 V:1.0 toR:&r G:&g B:&b];
  998. [self setLightsR:r G:g B:b];
  999. }
  1000. - (void)visualizeRandom:(NSTimer *)timer {
  1001. [self setLightsR:rand() % 256 G:rand() % 256 B:rand() % 256];
  1002. }
  1003. // -----------------------------------------------------
  1004. // --------------------- Utilities ---------------------
  1005. // -----------------------------------------------------
  1006. #ifdef COLORED_MENU_BAR_ICON
  1007. + (NSImage *)tintedImage:(NSImage *)image WithColor:(NSColor *)tint {
  1008. NSSize size = [image size];
  1009. NSRect imageBounds = NSMakeRect(0, 0, size.width, size.height);
  1010. NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
  1011. BOOL darkMode = ((osxMode != nil) && ([osxMode isEqualToString:@"Dark"])) ? YES : NO;
  1012. NSImage *copiedImage = [image copy];
  1013. static NSColor *lastTint = nil;
  1014. NSColor *copiedTint = nil;
  1015. if (tint != nil) {
  1016. // Just use the provided color
  1017. copiedTint = [tint copy];
  1018. lastTint = copiedTint;
  1019. } else {
  1020. // Try to use the same color as the last time
  1021. if (lastTint != nil) {
  1022. copiedTint = lastTint;
  1023. } else {
  1024. #ifdef DEBUG
  1025. NSLog(@"Trying to set last status bar icon color, but was not set!\n");
  1026. #endif
  1027. if (darkMode == YES) {
  1028. copiedTint = [NSColor whiteColor];
  1029. } else {
  1030. copiedTint = [NSColor blackColor];
  1031. }
  1032. }
  1033. }
  1034. // Fix colors for different interface styles
  1035. CGFloat r, g, b, a;
  1036. [copiedTint getRed:&r green:&g blue:&b alpha:&a];
  1037. if (darkMode == YES) {
  1038. if ((r < 0.001f) && (g < 0.001f) && (b < 0.001f)) {
  1039. copiedTint = [NSColor whiteColor];
  1040. }
  1041. } else {
  1042. if ((r > 0.999f) && (g > 0.999f) && (b > 0.999f)) {
  1043. copiedTint = [NSColor blackColor];
  1044. }
  1045. }
  1046. [copiedImage lockFocus];
  1047. [copiedTint set];
  1048. NSRectFillUsingOperation(imageBounds, NSCompositeSourceAtop);
  1049. [copiedImage unlockFocus];
  1050. return copiedImage;
  1051. }
  1052. #endif
  1053. + (double)map:(double)val FromMin:(double)fmin FromMax:(double)fmax ToMin:(double)tmin ToMax:(double)tmax {
  1054. double norm = (val - fmin) / (fmax - fmin);
  1055. return (norm * (tmax - tmin)) + tmin;
  1056. }
  1057. + (void)convertH:(double)h S:(double)s V:(double)v toR:(unsigned char *)r G:(unsigned char *)g B:(unsigned char *)b {
  1058. // Adapted from:
  1059. // https://gist.github.com/hdznrrd/656996
  1060. if (s == 0.0) {
  1061. // Achromatic
  1062. *r = *g = *b = (unsigned char)(v * 255);
  1063. return;
  1064. }
  1065. h /= 60; // sector 0 to 5
  1066. int i = floor(h);
  1067. double f = h - i; // factorial part of h
  1068. double p = v * (1 - s);
  1069. double q = v * (1 - s * f);
  1070. double t = v * (1 - s * (1 - f));
  1071. switch (i) {
  1072. case 0:
  1073. *r = (unsigned char)round(255 * v);
  1074. *g = (unsigned char)round(255 * t);
  1075. *b = (unsigned char)round(255 * p);
  1076. break;
  1077. case 1:
  1078. *r = (unsigned char)round(255 * q);
  1079. *g = (unsigned char)round(255 * v);
  1080. *b = (unsigned char)round(255 * p);
  1081. break;
  1082. case 2:
  1083. *r = (unsigned char)round(255 * p);
  1084. *g = (unsigned char)round(255 * v);
  1085. *b = (unsigned char)round(255 * t);
  1086. break;
  1087. case 3:
  1088. *r = (unsigned char)round(255 * p);
  1089. *g = (unsigned char)round(255 * q);
  1090. *b = (unsigned char)round(255 * v);
  1091. break;
  1092. case 4:
  1093. *r = (unsigned char)round(255 * t);
  1094. *g = (unsigned char)round(255 * p);
  1095. *b = (unsigned char)round(255 * v);
  1096. break;
  1097. default: case 5:
  1098. *r = (unsigned char)round(255 * v);
  1099. *g = (unsigned char)round(255 * p);
  1100. *b = (unsigned char)round(255 * q);
  1101. break;
  1102. }
  1103. }
  1104. @end