Browse Source

First (working) commit

Thomas Buck 7 years ago
commit
ab0eb3a993
No account linked to committer's email address
32 changed files with 2519 additions and 0 deletions
  1. 1
    0
      .gitignore
  2. 337
    0
      DisplayBacklight.xcodeproj/project.pbxproj
  3. 7
    0
      DisplayBacklight.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  4. 37
    0
      DisplayBacklight.xcodeproj/project.xcworkspace/xcshareddata/CaseLights.xcscmblueprint
  5. BIN
      DisplayBacklight.xcodeproj/project.xcworkspace/xcuserdata/thomas.xcuserdatad/UserInterfaceState.xcuserstate
  6. 5
    0
      DisplayBacklight.xcodeproj/xcuserdata/thomas.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
  7. 91
    0
      DisplayBacklight.xcodeproj/xcuserdata/thomas.xcuserdatad/xcschemes/DisplayBacklight.xcscheme
  8. 22
    0
      DisplayBacklight.xcodeproj/xcuserdata/thomas.xcuserdatad/xcschemes/xcschememanagement.plist
  9. 23
    0
      DisplayBacklight/AppDelegate.h
  10. 430
    0
      DisplayBacklight/AppDelegate.m
  11. BIN
      DisplayBacklight/Assets.xcassets/AppIcon.appiconset/1024.png
  12. BIN
      DisplayBacklight/Assets.xcassets/AppIcon.appiconset/128.png
  13. BIN
      DisplayBacklight/Assets.xcassets/AppIcon.appiconset/16.png
  14. BIN
      DisplayBacklight/Assets.xcassets/AppIcon.appiconset/256.png
  15. BIN
      DisplayBacklight/Assets.xcassets/AppIcon.appiconset/32.png
  16. BIN
      DisplayBacklight/Assets.xcassets/AppIcon.appiconset/512.png
  17. BIN
      DisplayBacklight/Assets.xcassets/AppIcon.appiconset/64.png
  18. 68
    0
      DisplayBacklight/Assets.xcassets/AppIcon.appiconset/Contents.json
  19. 6
    0
      DisplayBacklight/Assets.xcassets/Contents.json
  20. 23
    0
      DisplayBacklight/Assets.xcassets/MenuIcon.imageset/Contents.json
  21. BIN
      DisplayBacklight/Assets.xcassets/MenuIcon.imageset/icon_18.png
  22. BIN
      DisplayBacklight/Assets.xcassets/MenuIcon.imageset/icon_36.png
  23. BIN
      DisplayBacklight/Assets.xcassets/MenuIcon.imageset/icon_54.png
  24. 737
    0
      DisplayBacklight/Base.lproj/MainMenu.xib
  25. 38
    0
      DisplayBacklight/Info.plist
  26. 23
    0
      DisplayBacklight/Screenshot.h
  27. 144
    0
      DisplayBacklight/Screenshot.m
  28. 24
    0
      DisplayBacklight/Serial.h
  29. 309
    0
      DisplayBacklight/Serial.m
  30. 13
    0
      DisplayBacklight/main.m
  31. 138
    0
      LED-Strand-Test.ino
  32. 43
    0
      test.sh

+ 1
- 0
.gitignore View File

1
+.DS_Store

+ 337
- 0
DisplayBacklight.xcodeproj/project.pbxproj View File

1
+// !$*UTF8*$!
2
+{
3
+	archiveVersion = 1;
4
+	classes = {
5
+	};
6
+	objectVersion = 46;
7
+	objects = {
8
+
9
+/* Begin PBXBuildFile section */
10
+		E9640E8A1C30511B0081D46C /* Screenshot.m in Sources */ = {isa = PBXBuildFile; fileRef = E9640E891C30511B0081D46C /* Screenshot.m */; };
11
+		E9AB18011C29F8CD00BF3C51 /* Serial.m in Sources */ = {isa = PBXBuildFile; fileRef = E9AB18001C29F8CD00BF3C51 /* Serial.m */; };
12
+		E9F13E031C28B3D3004C6B95 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E9F13E021C28B3D3004C6B95 /* AppDelegate.m */; };
13
+		E9F13E061C28B3D3004C6B95 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E9F13E051C28B3D3004C6B95 /* main.m */; };
14
+		E9F13E081C28B3D3004C6B95 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E9F13E071C28B3D3004C6B95 /* Assets.xcassets */; };
15
+		E9F13E0B1C28B3D3004C6B95 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = E9F13E091C28B3D3004C6B95 /* MainMenu.xib */; };
16
+/* End PBXBuildFile section */
17
+
18
+/* Begin PBXCopyFilesBuildPhase section */
19
+		E9109B141C2AAEEC00726111 /* CopyFiles */ = {
20
+			isa = PBXCopyFilesBuildPhase;
21
+			buildActionMask = 2147483647;
22
+			dstPath = "";
23
+			dstSubfolderSpec = 10;
24
+			files = (
25
+			);
26
+			runOnlyForDeploymentPostprocessing = 0;
27
+		};
28
+/* End PBXCopyFilesBuildPhase section */
29
+
30
+/* Begin PBXFileReference section */
31
+		E9640E881C30511B0081D46C /* Screenshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Screenshot.h; sourceTree = "<group>"; };
32
+		E9640E891C30511B0081D46C /* Screenshot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Screenshot.m; sourceTree = "<group>"; };
33
+		E9AB17FF1C29F8CD00BF3C51 /* Serial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Serial.h; sourceTree = "<group>"; };
34
+		E9AB18001C29F8CD00BF3C51 /* Serial.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Serial.m; sourceTree = "<group>"; };
35
+		E9F13DFE1C28B3D3004C6B95 /* DisplayBacklight.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DisplayBacklight.app; sourceTree = BUILT_PRODUCTS_DIR; };
36
+		E9F13E011C28B3D3004C6B95 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
37
+		E9F13E021C28B3D3004C6B95 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
38
+		E9F13E051C28B3D3004C6B95 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
39
+		E9F13E071C28B3D3004C6B95 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
40
+		E9F13E0A1C28B3D3004C6B95 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
41
+		E9F13E0C1C28B3D3004C6B95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
42
+/* End PBXFileReference section */
43
+
44
+/* Begin PBXFrameworksBuildPhase section */
45
+		E9F13DFB1C28B3D3004C6B95 /* Frameworks */ = {
46
+			isa = PBXFrameworksBuildPhase;
47
+			buildActionMask = 2147483647;
48
+			files = (
49
+			);
50
+			runOnlyForDeploymentPostprocessing = 0;
51
+		};
52
+/* End PBXFrameworksBuildPhase section */
53
+
54
+/* Begin PBXGroup section */
55
+		E9F13DF51C28B3D3004C6B95 = {
56
+			isa = PBXGroup;
57
+			children = (
58
+				E9F13E001C28B3D3004C6B95 /* DisplayBacklight */,
59
+				E9F13DFF1C28B3D3004C6B95 /* Products */,
60
+			);
61
+			sourceTree = "<group>";
62
+		};
63
+		E9F13DFF1C28B3D3004C6B95 /* Products */ = {
64
+			isa = PBXGroup;
65
+			children = (
66
+				E9F13DFE1C28B3D3004C6B95 /* DisplayBacklight.app */,
67
+			);
68
+			name = Products;
69
+			sourceTree = "<group>";
70
+		};
71
+		E9F13E001C28B3D3004C6B95 /* DisplayBacklight */ = {
72
+			isa = PBXGroup;
73
+			children = (
74
+				E9F13E011C28B3D3004C6B95 /* AppDelegate.h */,
75
+				E9F13E021C28B3D3004C6B95 /* AppDelegate.m */,
76
+				E9AB17FF1C29F8CD00BF3C51 /* Serial.h */,
77
+				E9AB18001C29F8CD00BF3C51 /* Serial.m */,
78
+				E9640E881C30511B0081D46C /* Screenshot.h */,
79
+				E9640E891C30511B0081D46C /* Screenshot.m */,
80
+				E9F13E071C28B3D3004C6B95 /* Assets.xcassets */,
81
+				E9F13E091C28B3D3004C6B95 /* MainMenu.xib */,
82
+				E9F13E0C1C28B3D3004C6B95 /* Info.plist */,
83
+				E9F13E041C28B3D3004C6B95 /* Supporting Files */,
84
+			);
85
+			path = DisplayBacklight;
86
+			sourceTree = "<group>";
87
+		};
88
+		E9F13E041C28B3D3004C6B95 /* Supporting Files */ = {
89
+			isa = PBXGroup;
90
+			children = (
91
+				E9F13E051C28B3D3004C6B95 /* main.m */,
92
+			);
93
+			name = "Supporting Files";
94
+			sourceTree = "<group>";
95
+		};
96
+/* End PBXGroup section */
97
+
98
+/* Begin PBXNativeTarget section */
99
+		E9F13DFD1C28B3D3004C6B95 /* DisplayBacklight */ = {
100
+			isa = PBXNativeTarget;
101
+			buildConfigurationList = E9F13E0F1C28B3D3004C6B95 /* Build configuration list for PBXNativeTarget "DisplayBacklight" */;
102
+			buildPhases = (
103
+				E9AB17FC1C28B43B00BF3C51 /* Increment build number */,
104
+				E9F13DFA1C28B3D3004C6B95 /* Sources */,
105
+				E9F13DFB1C28B3D3004C6B95 /* Frameworks */,
106
+				E9F13DFC1C28B3D3004C6B95 /* Resources */,
107
+				E9109B141C2AAEEC00726111 /* CopyFiles */,
108
+			);
109
+			buildRules = (
110
+			);
111
+			dependencies = (
112
+			);
113
+			name = DisplayBacklight;
114
+			productName = CaseLights;
115
+			productReference = E9F13DFE1C28B3D3004C6B95 /* DisplayBacklight.app */;
116
+			productType = "com.apple.product-type.application";
117
+		};
118
+/* End PBXNativeTarget section */
119
+
120
+/* Begin PBXProject section */
121
+		E9F13DF61C28B3D3004C6B95 /* Project object */ = {
122
+			isa = PBXProject;
123
+			attributes = {
124
+				LastUpgradeCheck = 0720;
125
+				ORGANIZATIONNAME = xythobuz;
126
+				TargetAttributes = {
127
+					E9F13DFD1C28B3D3004C6B95 = {
128
+						CreatedOnToolsVersion = 7.2;
129
+					};
130
+				};
131
+			};
132
+			buildConfigurationList = E9F13DF91C28B3D3004C6B95 /* Build configuration list for PBXProject "DisplayBacklight" */;
133
+			compatibilityVersion = "Xcode 3.2";
134
+			developmentRegion = English;
135
+			hasScannedForEncodings = 0;
136
+			knownRegions = (
137
+				en,
138
+				Base,
139
+			);
140
+			mainGroup = E9F13DF51C28B3D3004C6B95;
141
+			productRefGroup = E9F13DFF1C28B3D3004C6B95 /* Products */;
142
+			projectDirPath = "";
143
+			projectRoot = "";
144
+			targets = (
145
+				E9F13DFD1C28B3D3004C6B95 /* DisplayBacklight */,
146
+			);
147
+		};
148
+/* End PBXProject section */
149
+
150
+/* Begin PBXResourcesBuildPhase section */
151
+		E9F13DFC1C28B3D3004C6B95 /* Resources */ = {
152
+			isa = PBXResourcesBuildPhase;
153
+			buildActionMask = 2147483647;
154
+			files = (
155
+				E9F13E081C28B3D3004C6B95 /* Assets.xcassets in Resources */,
156
+				E9F13E0B1C28B3D3004C6B95 /* MainMenu.xib in Resources */,
157
+			);
158
+			runOnlyForDeploymentPostprocessing = 0;
159
+		};
160
+/* End PBXResourcesBuildPhase section */
161
+
162
+/* Begin PBXShellScriptBuildPhase section */
163
+		E9AB17FC1C28B43B00BF3C51 /* Increment build number */ = {
164
+			isa = PBXShellScriptBuildPhase;
165
+			buildActionMask = 2147483647;
166
+			files = (
167
+			);
168
+			inputPaths = (
169
+			);
170
+			name = "Increment build number";
171
+			outputPaths = (
172
+			);
173
+			runOnlyForDeploymentPostprocessing = 0;
174
+			shellPath = /bin/sh;
175
+			shellScript = "#!/bin/sh\n\nplist=\"${PROJECT_DIR}/${INFOPLIST_FILE}\"\ndir=\"$(dirname \"$plist\")\"\n\n# Only increment the build number if source files have changed\nif [ -n \"$(find \"$dir\" \\! -path \"*xcuserdata*\" \\! -path \"*.git*\" \\! -name .DS_Store \\! -path \"*Website*\" -newer \"$plist\")\" ]; then\nbuildnum=$(/usr/libexec/Plistbuddy -c \"Print CFBundleVersion\" \"$plist\")\nif [ -z \"$buildnum\" ]; then\necho \"No build number in $plist\"\nexit 2\nfi\nbuildnum=$(expr $buildnum + 1)\n/usr/libexec/Plistbuddy -c \"Set CFBundleVersion $buildnum\" \"$plist\"\necho \"Incremented build number to $buildnum\"\nelse\necho \"Not incrementing build number as source files have not changed\"\nfi\n";
176
+		};
177
+/* End PBXShellScriptBuildPhase section */
178
+
179
+/* Begin PBXSourcesBuildPhase section */
180
+		E9F13DFA1C28B3D3004C6B95 /* Sources */ = {
181
+			isa = PBXSourcesBuildPhase;
182
+			buildActionMask = 2147483647;
183
+			files = (
184
+				E9AB18011C29F8CD00BF3C51 /* Serial.m in Sources */,
185
+				E9640E8A1C30511B0081D46C /* Screenshot.m in Sources */,
186
+				E9F13E061C28B3D3004C6B95 /* main.m in Sources */,
187
+				E9F13E031C28B3D3004C6B95 /* AppDelegate.m in Sources */,
188
+			);
189
+			runOnlyForDeploymentPostprocessing = 0;
190
+		};
191
+/* End PBXSourcesBuildPhase section */
192
+
193
+/* Begin PBXVariantGroup section */
194
+		E9F13E091C28B3D3004C6B95 /* MainMenu.xib */ = {
195
+			isa = PBXVariantGroup;
196
+			children = (
197
+				E9F13E0A1C28B3D3004C6B95 /* Base */,
198
+			);
199
+			name = MainMenu.xib;
200
+			sourceTree = "<group>";
201
+		};
202
+/* End PBXVariantGroup section */
203
+
204
+/* Begin XCBuildConfiguration section */
205
+		E9F13E0D1C28B3D3004C6B95 /* Debug */ = {
206
+			isa = XCBuildConfiguration;
207
+			buildSettings = {
208
+				ALWAYS_SEARCH_USER_PATHS = NO;
209
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
210
+				CLANG_CXX_LIBRARY = "libc++";
211
+				CLANG_ENABLE_MODULES = YES;
212
+				CLANG_ENABLE_OBJC_ARC = YES;
213
+				CLANG_WARN_BOOL_CONVERSION = YES;
214
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
215
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
216
+				CLANG_WARN_EMPTY_BODY = YES;
217
+				CLANG_WARN_ENUM_CONVERSION = YES;
218
+				CLANG_WARN_INT_CONVERSION = YES;
219
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
220
+				CLANG_WARN_UNREACHABLE_CODE = YES;
221
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
222
+				CODE_SIGN_IDENTITY = "-";
223
+				COPY_PHASE_STRIP = NO;
224
+				DEBUG_INFORMATION_FORMAT = dwarf;
225
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
226
+				ENABLE_TESTABILITY = YES;
227
+				GCC_C_LANGUAGE_STANDARD = gnu99;
228
+				GCC_DYNAMIC_NO_PIC = NO;
229
+				GCC_NO_COMMON_BLOCKS = YES;
230
+				GCC_OPTIMIZATION_LEVEL = 0;
231
+				GCC_PREPROCESSOR_DEFINITIONS = (
232
+					"DEBUG=1",
233
+					"$(inherited)",
234
+				);
235
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
236
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
237
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
238
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
239
+				GCC_WARN_UNUSED_FUNCTION = YES;
240
+				GCC_WARN_UNUSED_VARIABLE = YES;
241
+				MACOSX_DEPLOYMENT_TARGET = 10.8;
242
+				MTL_ENABLE_DEBUG_INFO = YES;
243
+				ONLY_ACTIVE_ARCH = YES;
244
+				SDKROOT = macosx;
245
+			};
246
+			name = Debug;
247
+		};
248
+		E9F13E0E1C28B3D3004C6B95 /* Release */ = {
249
+			isa = XCBuildConfiguration;
250
+			buildSettings = {
251
+				ALWAYS_SEARCH_USER_PATHS = NO;
252
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
253
+				CLANG_CXX_LIBRARY = "libc++";
254
+				CLANG_ENABLE_MODULES = YES;
255
+				CLANG_ENABLE_OBJC_ARC = YES;
256
+				CLANG_WARN_BOOL_CONVERSION = YES;
257
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
258
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
259
+				CLANG_WARN_EMPTY_BODY = YES;
260
+				CLANG_WARN_ENUM_CONVERSION = YES;
261
+				CLANG_WARN_INT_CONVERSION = YES;
262
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
263
+				CLANG_WARN_UNREACHABLE_CODE = YES;
264
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
265
+				CODE_SIGN_IDENTITY = "-";
266
+				COPY_PHASE_STRIP = NO;
267
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
268
+				ENABLE_NS_ASSERTIONS = NO;
269
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
270
+				GCC_C_LANGUAGE_STANDARD = gnu99;
271
+				GCC_NO_COMMON_BLOCKS = YES;
272
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
273
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
274
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
275
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
276
+				GCC_WARN_UNUSED_FUNCTION = YES;
277
+				GCC_WARN_UNUSED_VARIABLE = YES;
278
+				MACOSX_DEPLOYMENT_TARGET = 10.8;
279
+				MTL_ENABLE_DEBUG_INFO = NO;
280
+				SDKROOT = macosx;
281
+			};
282
+			name = Release;
283
+		};
284
+		E9F13E101C28B3D3004C6B95 /* Debug */ = {
285
+			isa = XCBuildConfiguration;
286
+			buildSettings = {
287
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
288
+				COMBINE_HIDPI_IMAGES = YES;
289
+				INFOPLIST_FILE = DisplayBacklight/Info.plist;
290
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
291
+				MACOSX_DEPLOYMENT_TARGET = 10.8;
292
+				PRODUCT_BUNDLE_IDENTIFIER = de.xythobuz.DisplayBacklight;
293
+				PRODUCT_NAME = DisplayBacklight;
294
+				USER_HEADER_SEARCH_PATHS = "JSystemInfoKit/** EZAudio/EZAudio/**";
295
+			};
296
+			name = Debug;
297
+		};
298
+		E9F13E111C28B3D3004C6B95 /* Release */ = {
299
+			isa = XCBuildConfiguration;
300
+			buildSettings = {
301
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
302
+				COMBINE_HIDPI_IMAGES = YES;
303
+				HEADER_SEARCH_PATHS = "";
304
+				INFOPLIST_FILE = DisplayBacklight/Info.plist;
305
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
306
+				MACOSX_DEPLOYMENT_TARGET = 10.8;
307
+				PRODUCT_BUNDLE_IDENTIFIER = de.xythobuz.DisplayBacklight;
308
+				PRODUCT_NAME = DisplayBacklight;
309
+				USER_HEADER_SEARCH_PATHS = "JSystemInfoKit/** EZAudio/EZAudio/**";
310
+			};
311
+			name = Release;
312
+		};
313
+/* End XCBuildConfiguration section */
314
+
315
+/* Begin XCConfigurationList section */
316
+		E9F13DF91C28B3D3004C6B95 /* Build configuration list for PBXProject "DisplayBacklight" */ = {
317
+			isa = XCConfigurationList;
318
+			buildConfigurations = (
319
+				E9F13E0D1C28B3D3004C6B95 /* Debug */,
320
+				E9F13E0E1C28B3D3004C6B95 /* Release */,
321
+			);
322
+			defaultConfigurationIsVisible = 0;
323
+			defaultConfigurationName = Release;
324
+		};
325
+		E9F13E0F1C28B3D3004C6B95 /* Build configuration list for PBXNativeTarget "DisplayBacklight" */ = {
326
+			isa = XCConfigurationList;
327
+			buildConfigurations = (
328
+				E9F13E101C28B3D3004C6B95 /* Debug */,
329
+				E9F13E111C28B3D3004C6B95 /* Release */,
330
+			);
331
+			defaultConfigurationIsVisible = 0;
332
+			defaultConfigurationName = Release;
333
+		};
334
+/* End XCConfigurationList section */
335
+	};
336
+	rootObject = E9F13DF61C28B3D3004C6B95 /* Project object */;
337
+}

+ 7
- 0
DisplayBacklight.xcodeproj/project.xcworkspace/contents.xcworkspacedata View File

1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<Workspace
3
+   version = "1.0">
4
+   <FileRef
5
+      location = "self:/Users/thomas/Projekte/DisplayBacklight/DisplayBacklight.xcodeproj">
6
+   </FileRef>
7
+</Workspace>

+ 37
- 0
DisplayBacklight.xcodeproj/project.xcworkspace/xcshareddata/CaseLights.xcscmblueprint View File

1
+{
2
+  "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "AB48BACB8946F305FD9B21DE9855B0A6C83AC703",
3
+  "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4
+
5
+  },
6
+  "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7
+    "10CC2F1E3D4AA1768B20E4ECDF5C6C9B76C1AE64" : 0,
8
+    "AB48BACB8946F305FD9B21DE9855B0A6C83AC703" : 0,
9
+    "9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C" : 0
10
+  },
11
+  "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "00D44A58-6D35-4699-B211-AA592656F9E4",
12
+  "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
13
+    "10CC2F1E3D4AA1768B20E4ECDF5C6C9B76C1AE64" : "CaseLights\/JSystemInfoKit\/",
14
+    "AB48BACB8946F305FD9B21DE9855B0A6C83AC703" : "CaseLights\/",
15
+    "9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C" : "CaseLights\/EZAudio\/"
16
+  },
17
+  "DVTSourceControlWorkspaceBlueprintNameKey" : "CaseLights",
18
+  "DVTSourceControlWorkspaceBlueprintVersion" : 204,
19
+  "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CaseLights.xcodeproj",
20
+  "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
21
+    {
22
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/xythobuz\/JSystemInfoKit.git",
23
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
24
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "10CC2F1E3D4AA1768B20E4ECDF5C6C9B76C1AE64"
25
+    },
26
+    {
27
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/syedhali\/EZAudio.git",
28
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
29
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C"
30
+    },
31
+    {
32
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "xythobuz.de:\/home\/thomas\/CaseLights.git",
33
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
34
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "AB48BACB8946F305FD9B21DE9855B0A6C83AC703"
35
+    }
36
+  ]
37
+}

BIN
DisplayBacklight.xcodeproj/project.xcworkspace/xcuserdata/thomas.xcuserdatad/UserInterfaceState.xcuserstate View File


+ 5
- 0
DisplayBacklight.xcodeproj/xcuserdata/thomas.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist View File

1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<Bucket
3
+   type = "1"
4
+   version = "2.0">
5
+</Bucket>

+ 91
- 0
DisplayBacklight.xcodeproj/xcuserdata/thomas.xcuserdatad/xcschemes/DisplayBacklight.xcscheme View File

1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<Scheme
3
+   LastUpgradeVersion = "0720"
4
+   version = "1.3">
5
+   <BuildAction
6
+      parallelizeBuildables = "YES"
7
+      buildImplicitDependencies = "YES">
8
+      <BuildActionEntries>
9
+         <BuildActionEntry
10
+            buildForTesting = "YES"
11
+            buildForRunning = "YES"
12
+            buildForProfiling = "YES"
13
+            buildForArchiving = "YES"
14
+            buildForAnalyzing = "YES">
15
+            <BuildableReference
16
+               BuildableIdentifier = "primary"
17
+               BlueprintIdentifier = "E9F13DFD1C28B3D3004C6B95"
18
+               BuildableName = "DisplayBacklight.app"
19
+               BlueprintName = "DisplayBacklight"
20
+               ReferencedContainer = "container:DisplayBacklight.xcodeproj">
21
+            </BuildableReference>
22
+         </BuildActionEntry>
23
+      </BuildActionEntries>
24
+   </BuildAction>
25
+   <TestAction
26
+      buildConfiguration = "Debug"
27
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29
+      shouldUseLaunchSchemeArgsEnv = "YES">
30
+      <Testables>
31
+      </Testables>
32
+      <MacroExpansion>
33
+         <BuildableReference
34
+            BuildableIdentifier = "primary"
35
+            BlueprintIdentifier = "E9F13DFD1C28B3D3004C6B95"
36
+            BuildableName = "DisplayBacklight.app"
37
+            BlueprintName = "DisplayBacklight"
38
+            ReferencedContainer = "container:DisplayBacklight.xcodeproj">
39
+         </BuildableReference>
40
+      </MacroExpansion>
41
+      <AdditionalOptions>
42
+      </AdditionalOptions>
43
+   </TestAction>
44
+   <LaunchAction
45
+      buildConfiguration = "Debug"
46
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
47
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
48
+      launchStyle = "0"
49
+      useCustomWorkingDirectory = "NO"
50
+      ignoresPersistentStateOnLaunch = "NO"
51
+      debugDocumentVersioning = "YES"
52
+      debugServiceExtension = "internal"
53
+      allowLocationSimulation = "YES">
54
+      <BuildableProductRunnable
55
+         runnableDebuggingMode = "0">
56
+         <BuildableReference
57
+            BuildableIdentifier = "primary"
58
+            BlueprintIdentifier = "E9F13DFD1C28B3D3004C6B95"
59
+            BuildableName = "DisplayBacklight.app"
60
+            BlueprintName = "DisplayBacklight"
61
+            ReferencedContainer = "container:DisplayBacklight.xcodeproj">
62
+         </BuildableReference>
63
+      </BuildableProductRunnable>
64
+      <AdditionalOptions>
65
+      </AdditionalOptions>
66
+   </LaunchAction>
67
+   <ProfileAction
68
+      buildConfiguration = "Release"
69
+      shouldUseLaunchSchemeArgsEnv = "YES"
70
+      savedToolIdentifier = ""
71
+      useCustomWorkingDirectory = "NO"
72
+      debugDocumentVersioning = "YES">
73
+      <BuildableProductRunnable
74
+         runnableDebuggingMode = "0">
75
+         <BuildableReference
76
+            BuildableIdentifier = "primary"
77
+            BlueprintIdentifier = "E9F13DFD1C28B3D3004C6B95"
78
+            BuildableName = "DisplayBacklight.app"
79
+            BlueprintName = "DisplayBacklight"
80
+            ReferencedContainer = "container:DisplayBacklight.xcodeproj">
81
+         </BuildableReference>
82
+      </BuildableProductRunnable>
83
+   </ProfileAction>
84
+   <AnalyzeAction
85
+      buildConfiguration = "Debug">
86
+   </AnalyzeAction>
87
+   <ArchiveAction
88
+      buildConfiguration = "Release"
89
+      revealArchiveInOrganizer = "YES">
90
+   </ArchiveAction>
91
+</Scheme>

+ 22
- 0
DisplayBacklight.xcodeproj/xcuserdata/thomas.xcuserdatad/xcschemes/xcschememanagement.plist View File

1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>SchemeUserState</key>
6
+	<dict>
7
+		<key>DisplayBacklight.xcscheme</key>
8
+		<dict>
9
+			<key>orderHint</key>
10
+			<integer>2</integer>
11
+		</dict>
12
+	</dict>
13
+	<key>SuppressBuildableAutocreation</key>
14
+	<dict>
15
+		<key>E9F13DFD1C28B3D3004C6B95</key>
16
+		<dict>
17
+			<key>primary</key>
18
+			<true/>
19
+		</dict>
20
+	</dict>
21
+</dict>
22
+</plist>

+ 23
- 0
DisplayBacklight/AppDelegate.h View File

1
+//
2
+//  AppDelegate.h
3
+//  DisplayBacklight
4
+//
5
+//  Created by Thomas Buck on 21.12.15.
6
+//  Copyright © 2015 xythobuz. All rights reserved.
7
+//
8
+
9
+#import <Cocoa/Cocoa.h>
10
+
11
+@class Serial;
12
+
13
+@interface AppDelegate : NSObject <NSApplicationDelegate>
14
+
15
+@property (weak) IBOutlet NSApplication *application;
16
+
17
+- (void)stopAmbilight;
18
+- (void)newDisplayList:(NSArray *)displayIDs;
19
+
20
++ (double)map:(double)val FromMin:(double)fmin FromMax:(double)fmax ToMin:(double)tmin ToMax:(double)tmax;
21
+
22
+@end
23
+

+ 430
- 0
DisplayBacklight/AppDelegate.m View File

1
+//
2
+//  AppDelegate.m
3
+//  DisplayBacklight
4
+//
5
+//  Created by Thomas Buck on 21.12.15.
6
+//  Copyright © 2015 xythobuz. All rights reserved.
7
+//
8
+
9
+#import "AppDelegate.h"
10
+#import "Serial.h"
11
+#import "Screenshot.h"
12
+
13
+// These are the values stored persistently in the preferences
14
+#define PREF_SERIAL_PORT @"SerialPort"
15
+#define PREF_BRIGHTNESS @"Brightness"
16
+
17
+#define DISPLAY_DELAY (1.0 / 10.0)
18
+
19
+@interface AppDelegate ()
20
+
21
+@property (weak) IBOutlet NSMenu *statusMenu;
22
+@property (weak) IBOutlet NSMenu *menuPorts;
23
+@property (weak) IBOutlet NSMenuItem *buttonAmbilight;
24
+@property (weak) IBOutlet NSMenuItem *brightnessItem;
25
+@property (weak) IBOutlet NSSlider *brightnessSlider;
26
+@property (weak) IBOutlet NSMenuItem *brightnessLabel;
27
+
28
+@property (strong) NSStatusItem *statusItem;
29
+@property (strong) NSImage *statusImage;
30
+@property (strong) NSTimer *timer;
31
+@property (strong) Serial *serial;
32
+@property (strong) NSArray *lastDisplayIDs;
33
+@property (assign) BOOL restartAmbilight;
34
+
35
+@end
36
+
37
+@implementation AppDelegate
38
+
39
+@synthesize statusMenu, application;
40
+@synthesize menuPorts, buttonAmbilight;
41
+@synthesize brightnessItem, brightnessSlider, brightnessLabel;
42
+@synthesize statusItem, statusImage, lastDisplayIDs;
43
+@synthesize timer, serial, restartAmbilight;
44
+
45
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
46
+    serial = [[Serial alloc] init];
47
+    timer = nil;
48
+    restartAmbilight = NO;
49
+    
50
+    // Set default configuration values
51
+    NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
52
+    NSMutableDictionary *appDefaults = [NSMutableDictionary dictionaryWithObject:@"" forKey:PREF_SERIAL_PORT];
53
+    [appDefaults setObject:[NSNumber numberWithFloat:50.0] forKey:PREF_BRIGHTNESS];
54
+    [store registerDefaults:appDefaults];
55
+    [store synchronize];
56
+    
57
+    // Load existing configuration values
58
+    NSString *savedPort = [store stringForKey:PREF_SERIAL_PORT];
59
+    float brightness = [store floatForKey:PREF_BRIGHTNESS];
60
+    
61
+    // Prepare status bar menu
62
+    statusImage = [NSImage imageNamed:@"MenuIcon"];
63
+    statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
64
+    [statusImage setTemplate:YES];
65
+    [statusItem setImage:statusImage];
66
+    [statusItem setMenu:statusMenu];
67
+    
68
+    // Prepare brightness menu
69
+    brightnessItem.view = brightnessSlider;
70
+    [brightnessSlider setFloatValue:brightness];
71
+    [brightnessLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", brightness]];
72
+    
73
+    // Prepare serial port menu
74
+    BOOL startTimer = NO;
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
+            
83
+            // Set Enabled if it was used the last time
84
+            if ((savedPort != nil) && [[ports objectAtIndex:i] isEqualToString:savedPort]) {
85
+                [[menuPorts itemAtIndex:i] setState:NSOnState];
86
+                
87
+                // Try to open serial port
88
+                [serial setPortName:savedPort];
89
+                if ([serial openPort]) {
90
+                    // Unselect it when an error occured opening the port
91
+                    [[menuPorts itemAtIndex:i] setState:NSOffState];
92
+                } else {
93
+                    startTimer = YES;
94
+                }
95
+            }
96
+        }
97
+        
98
+        if (!startTimer) {
99
+            // TODO try to find out new UART port name for controller
100
+        }
101
+    }
102
+    
103
+    [Screenshot init:self];
104
+    lastDisplayIDs = [Screenshot listDisplays];
105
+    
106
+    if (startTimer) {
107
+        timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:YES];
108
+        
109
+        [buttonAmbilight setState:NSOnState];
110
+    }
111
+}
112
+
113
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
114
+    // Stop previous timer setting
115
+    if (timer != nil) {
116
+        [timer invalidate];
117
+        timer = nil;
118
+    }
119
+    
120
+    // Remove display callback
121
+    [Screenshot close:self];
122
+    
123
+    // Turn off all lights if possible
124
+    if ([serial isOpen]) {
125
+        [self sendNullFrame];
126
+        [serial closePort];
127
+    }
128
+}
129
+
130
+- (IBAction)relistSerialPorts:(id)sender {
131
+    // Refill port list
132
+    NSArray *ports = [Serial listSerialPorts];
133
+    [menuPorts removeAllItems];
134
+    for (int i = 0; i < [ports count]; i++) {
135
+        // Add Menu Item for this port
136
+        NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[ports objectAtIndex:i] action:@selector(selectedSerialPort:) keyEquivalent:@""];
137
+        [menuPorts addItem:item];
138
+        
139
+        // Mark it if it is currently open
140
+        if ([serial isOpen]) {
141
+            if ([[ports objectAtIndex:i] isEqualToString:[serial portName]]) {
142
+                [[menuPorts itemAtIndex:i] setState:NSOnState];
143
+            }
144
+        }
145
+    }
146
+}
147
+
148
+- (void)selectedSerialPort:(NSMenuItem *)source {
149
+    // Store selection for next start-up
150
+    NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
151
+    [store setObject:[source title] forKey:PREF_SERIAL_PORT];
152
+    [store synchronize];
153
+    
154
+    // De-select all other ports
155
+    for (int i = 0; i < [menuPorts numberOfItems]; i++) {
156
+        [[menuPorts itemAtIndex:i] setState:NSOffState];
157
+    }
158
+    
159
+    // Select only the current port
160
+    [source setState:NSOnState];
161
+    
162
+    // Close previously opened port, if any
163
+    if ([serial isOpen]) {
164
+        [serial closePort];
165
+    }
166
+    
167
+    // Stop previous timer setting
168
+    if (timer != nil) {
169
+        [timer invalidate];
170
+        timer = nil;
171
+    }
172
+    
173
+    // Turn off ambilight button
174
+    [buttonAmbilight setState:NSOffState];
175
+    
176
+    // Try to open selected port
177
+    [serial setPortName:[source title]];
178
+    if ([serial openPort] != 0) {
179
+        [source setState:NSOffState];
180
+    }
181
+}
182
+
183
+- (IBAction)toggleAmbilight:(NSMenuItem *)sender {
184
+    if ([sender state] == NSOnState) {
185
+        [sender setState:NSOffState];
186
+        
187
+        // Stop previous timer setting
188
+        if (timer != nil) {
189
+            [timer invalidate];
190
+            timer = nil;
191
+        }
192
+    } else {
193
+        [sender setState:NSOnState];
194
+        
195
+        timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:YES];
196
+    }
197
+}
198
+
199
+- (void)stopAmbilight {
200
+    restartAmbilight = NO;
201
+    if (timer != nil) {
202
+        restartAmbilight = YES;
203
+        [timer invalidate];
204
+        timer = nil;
205
+        
206
+        [buttonAmbilight setState:NSOffState];
207
+    }
208
+}
209
+
210
+- (void)newDisplayList:(NSArray *)displayIDs {
211
+    lastDisplayIDs = displayIDs;
212
+    
213
+    if (restartAmbilight) {
214
+        restartAmbilight = NO;
215
+        [buttonAmbilight setState:NSOnState];
216
+        
217
+        timer = [NSTimer scheduledTimerWithTimeInterval:DISPLAY_DELAY target:self selector:@selector(visualizeDisplay:) userInfo:nil repeats:YES];
218
+    }
219
+}
220
+
221
+- (IBAction)brightnessMoved:(NSSlider *)sender {
222
+    [brightnessLabel setTitle:[NSString stringWithFormat:@"Value: %.0f%%", [sender floatValue]]];
223
+    
224
+    // Store changed value in preferences
225
+    NSUserDefaults *store = [NSUserDefaults standardUserDefaults];
226
+    [store setObject:[NSNumber numberWithFloat:[sender floatValue]] forKey:PREF_BRIGHTNESS];
227
+    [store synchronize];
228
+}
229
+
230
+- (IBAction)showAbout:(id)sender {
231
+    [NSApp activateIgnoringOtherApps:YES];
232
+    [application orderFrontStandardAboutPanel:self];
233
+}
234
+
235
+- (UInt32)calculateAverage:(unsigned char *)data Width:(NSInteger)width Height:(NSInteger)height SPP:(NSInteger)spp Alpha:(BOOL)alpha StartX:(NSInteger)startX StartY:(NSInteger)startY EndX:(NSInteger)endX EndY:(NSInteger)endY {
236
+    int redC = 0, greenC = 1, blueC = 2;
237
+    if (alpha) {
238
+        redC = 1; greenC = 2; blueC = 3;
239
+    }
240
+    
241
+    NSInteger xa, xb, ya, yb;
242
+    if (startX < endX) {
243
+        xa = startX;
244
+        xb = endX;
245
+    } else {
246
+        xa = endX;
247
+        xb = startX;
248
+    }
249
+    if (startY < endY) {
250
+        ya = startY;
251
+        yb = endY;
252
+    } else {
253
+        ya = endY;
254
+        yb = startY;
255
+    }
256
+    
257
+    unsigned long red = 0, green = 0, blue = 0, count = 0;
258
+    for (NSInteger i = xa; i < xb; i++) {
259
+        for (NSInteger j = ya; j < yb; j++) {
260
+            count++;
261
+            unsigned long index = i + (j * width);
262
+            red += data[(index * spp) + redC];
263
+            green += data[(index * spp) + greenC];
264
+            blue += data[(index * spp) + blueC];
265
+        }
266
+    }
267
+    red /= count;
268
+    green /= count;
269
+    blue /= count;
270
+    
271
+    return ((UInt32)red << 16) | ((UInt32)green << 8) | ((UInt32)blue);
272
+}
273
+
274
+struct DisplayAssignment {
275
+    int n;
276
+    int width, height;
277
+};
278
+
279
+struct LEDStrand {
280
+    int idMin, idMax;
281
+    int display;
282
+    int startX, startY;
283
+    int direction;
284
+    int size;
285
+};
286
+
287
+#define DIR_LEFT 0
288
+#define DIR_RIGHT 1
289
+#define DIR_UP 2
290
+#define DIR_DOWN 3
291
+
292
+// TODO remove first
293
+struct DisplayAssignment displays[] = {
294
+    { 0, 1920, 1080 },
295
+    { 1,  900, 1600 }
296
+};
297
+
298
+struct LEDStrand strands[] = {
299
+    {   0,  32, 0, 1920, 1080,  DIR_LEFT, 1920 / 33 },
300
+    {  33,  51, 0,    0, 1080,    DIR_UP, 1080 / 19 },
301
+    {  52,  84, 0,    0,    0, DIR_RIGHT, 1920 / 33 },
302
+    {  85,  89, 1,    0,  250,    DIR_UP,  250 / 5 },
303
+    {  90, 106, 1,    0,    0, DIR_RIGHT,  900 / 17 },
304
+    { 107, 134, 1,  900,    0,  DIR_DOWN, 1600 / 28 },
305
+    { 135, 151, 1,  900, 1600,  DIR_LEFT,  900 / 17 },
306
+    { 152, 155, 1,    0, 1600,    DIR_UP,  180 / 4 }
307
+};
308
+
309
+UInt8 ledColorData[156 * 3];
310
+
311
+#define COLOR_AVERAGE_OTHER_DIMENSION_SIZE 150
312
+
313
+- (void)visualizeSingleDisplay:(NSInteger)disp Data:(unsigned char *)data Width:(unsigned long)width Height:(unsigned long)height SPP:(NSInteger)spp Alpha:(BOOL)alpha {
314
+    for (int i = 0; i < (sizeof(strands) / sizeof(strands[0])); i++) {
315
+        if (strands[i].display == disp) {
316
+            // Walk the strand, calculating value for each LED
317
+            unsigned long x = strands[i].startX;
318
+            unsigned long y = strands[i].startY;
319
+            unsigned long blockWidth = COLOR_AVERAGE_OTHER_DIMENSION_SIZE;
320
+            unsigned long blockHeight = COLOR_AVERAGE_OTHER_DIMENSION_SIZE;
321
+
322
+            if ((strands[i].direction == DIR_LEFT) || (strands[i].direction == DIR_RIGHT)) {
323
+                blockWidth = strands[i].size;
324
+            } else {
325
+                blockHeight = strands[i].size;
326
+            }
327
+            
328
+            for (int led = strands[i].idMin; led <= strands[i].idMax; led++) {
329
+                // First move appropriately in the direction of the strand
330
+                unsigned long endX = x, endY = y;
331
+                if (strands[i].direction == DIR_LEFT) {
332
+                    endX -= blockWidth;
333
+                } else if (strands[i].direction == DIR_RIGHT) {
334
+                    endX += blockWidth;
335
+                } else if (strands[i].direction == DIR_UP) {
336
+                    endY -= blockHeight;
337
+                } else if (strands[i].direction == DIR_DOWN) {
338
+                    endY += blockHeight;
339
+                }
340
+                
341
+                // But also span the averaging-square in the other dimension, depending on which
342
+                // side of the monitor we're at.
343
+                if ((strands[i].direction == DIR_LEFT) || (strands[i].direction == DIR_RIGHT)) {
344
+                    if (y == 0) {
345
+                        endY = blockHeight;
346
+                    } else if (y == displays[disp].height) {
347
+                        endY -= blockHeight;
348
+                    }
349
+                } else {
350
+                    if (x == 0) {
351
+                        endX = blockWidth;
352
+                    } else if (x == displays[disp].width) {
353
+                        endX -= blockWidth;
354
+                    }
355
+                }
356
+                
357
+                // Calculate average color for this led
358
+                UInt32 color = [self calculateAverage:data Width:width Height:height SPP:spp Alpha:alpha StartX:x StartY:y EndX:endX EndY:endY];
359
+                
360
+                
361
+                ledColorData[led * 3] = (color & 0xFF0000) >> 16;
362
+                ledColorData[(led * 3) + 1] = (color & 0x00FF00) >> 8;
363
+                ledColorData[(led * 3) + 2] = color & 0x0000FF;
364
+                
365
+                // Move to next LED
366
+                if ((strands[i].direction == DIR_LEFT) || (strands[i].direction == DIR_RIGHT)) {
367
+                    x = endX;
368
+                } else {
369
+                    y = endY;
370
+                }
371
+            }
372
+        }
373
+    }
374
+}
375
+
376
+- (void)visualizeDisplay:(NSTimer *)time {
377
+    //NSLog(@"Running Ambilight-Algorithm (%lu)...", (unsigned long)[lastDisplayIDs count]);
378
+    
379
+    // Create a Screenshot for all connected displays
380
+    for (NSInteger i = 0; i < [lastDisplayIDs count]; i++) {
381
+        NSBitmapImageRep *screen = [Screenshot screenshot:[lastDisplayIDs objectAtIndex:i]];
382
+        unsigned long width = [screen pixelsWide];
383
+        unsigned long height = [screen pixelsHigh];
384
+        
385
+        // Ensure we can handle the format of this display
386
+        NSInteger spp = [screen samplesPerPixel];
387
+        if (((spp != 3) && (spp != 4)) || ([screen isPlanar] == YES) || ([screen numberOfPlanes] != 1)) {
388
+            NSLog(@"Unknown image format for %ld (%ld, %c, %ld)!\n", (long)i, (long)spp, ([screen isPlanar] == YES) ? 'p' : 'n', (long)[screen numberOfPlanes]);
389
+            continue;
390
+        }
391
+        
392
+        // Find out how the color components are ordered
393
+        BOOL alpha = NO;
394
+        if ([screen bitmapFormat] & NSAlphaFirstBitmapFormat) {
395
+            alpha = YES;
396
+        }
397
+        
398
+        // Try to find the matching display id for the strand associations
399
+        for (int n = 0; n < (sizeof(displays) / sizeof(displays[0])); n++) {
400
+            if ((width == displays[n].width) && (height == displays[n].height)) {
401
+                unsigned char *data = [screen bitmapData];
402
+                [self visualizeSingleDisplay:n Data:data Width:width Height:height SPP:spp Alpha:alpha];
403
+                break;
404
+            }
405
+        }
406
+    }
407
+    
408
+    [self sendLEDFrame];
409
+}
410
+
411
+- (void)sendLEDFrame {
412
+    if ([serial isOpen]) {
413
+        [serial sendString:@"xythobuzRGBled"];
414
+        [serial sendData:(char *)ledColorData withLength:(sizeof(ledColorData) / sizeof(ledColorData[0]))];
415
+    }
416
+}
417
+
418
+- (void)sendNullFrame {
419
+    for (int i = 0; i < (sizeof(ledColorData) / sizeof(ledColorData[0])); i++) {
420
+        ledColorData[i] = 0;
421
+    }
422
+    [self sendLEDFrame];
423
+}
424
+
425
++ (double)map:(double)val FromMin:(double)fmin FromMax:(double)fmax ToMin:(double)tmin ToMax:(double)tmax {
426
+    double norm = (val - fmin) / (fmax - fmin);
427
+    return (norm * (tmax - tmin)) + tmin;
428
+}
429
+
430
+@end

BIN
DisplayBacklight/Assets.xcassets/AppIcon.appiconset/1024.png View File


BIN
DisplayBacklight/Assets.xcassets/AppIcon.appiconset/128.png View File


BIN
DisplayBacklight/Assets.xcassets/AppIcon.appiconset/16.png View File


BIN
DisplayBacklight/Assets.xcassets/AppIcon.appiconset/256.png View File


BIN
DisplayBacklight/Assets.xcassets/AppIcon.appiconset/32.png View File


BIN
DisplayBacklight/Assets.xcassets/AppIcon.appiconset/512.png View File


BIN
DisplayBacklight/Assets.xcassets/AppIcon.appiconset/64.png View File


+ 68
- 0
DisplayBacklight/Assets.xcassets/AppIcon.appiconset/Contents.json View File

1
+{
2
+  "images" : [
3
+    {
4
+      "size" : "16x16",
5
+      "idiom" : "mac",
6
+      "filename" : "16.png",
7
+      "scale" : "1x"
8
+    },
9
+    {
10
+      "size" : "16x16",
11
+      "idiom" : "mac",
12
+      "filename" : "32.png",
13
+      "scale" : "2x"
14
+    },
15
+    {
16
+      "size" : "32x32",
17
+      "idiom" : "mac",
18
+      "filename" : "32.png",
19
+      "scale" : "1x"
20
+    },
21
+    {
22
+      "size" : "32x32",
23
+      "idiom" : "mac",
24
+      "filename" : "64.png",
25
+      "scale" : "2x"
26
+    },
27
+    {
28
+      "size" : "128x128",
29
+      "idiom" : "mac",
30
+      "filename" : "128.png",
31
+      "scale" : "1x"
32
+    },
33
+    {
34
+      "size" : "128x128",
35
+      "idiom" : "mac",
36
+      "filename" : "256.png",
37
+      "scale" : "2x"
38
+    },
39
+    {
40
+      "size" : "256x256",
41
+      "idiom" : "mac",
42
+      "filename" : "256.png",
43
+      "scale" : "1x"
44
+    },
45
+    {
46
+      "size" : "256x256",
47
+      "idiom" : "mac",
48
+      "filename" : "512.png",
49
+      "scale" : "2x"
50
+    },
51
+    {
52
+      "size" : "512x512",
53
+      "idiom" : "mac",
54
+      "filename" : "512.png",
55
+      "scale" : "1x"
56
+    },
57
+    {
58
+      "size" : "512x512",
59
+      "idiom" : "mac",
60
+      "filename" : "1024.png",
61
+      "scale" : "2x"
62
+    }
63
+  ],
64
+  "info" : {
65
+    "version" : 1,
66
+    "author" : "xcode"
67
+  }
68
+}

+ 6
- 0
DisplayBacklight/Assets.xcassets/Contents.json View File

1
+{
2
+  "info" : {
3
+    "version" : 1,
4
+    "author" : "xcode"
5
+  }
6
+}

+ 23
- 0
DisplayBacklight/Assets.xcassets/MenuIcon.imageset/Contents.json View File

1
+{
2
+  "images" : [
3
+    {
4
+      "idiom" : "universal",
5
+      "filename" : "icon_18.png",
6
+      "scale" : "1x"
7
+    },
8
+    {
9
+      "idiom" : "universal",
10
+      "filename" : "icon_36.png",
11
+      "scale" : "2x"
12
+    },
13
+    {
14
+      "idiom" : "universal",
15
+      "filename" : "icon_54.png",
16
+      "scale" : "3x"
17
+    }
18
+  ],
19
+  "info" : {
20
+    "version" : 1,
21
+    "author" : "xcode"
22
+  }
23
+}

BIN
DisplayBacklight/Assets.xcassets/MenuIcon.imageset/icon_18.png View File


BIN
DisplayBacklight/Assets.xcassets/MenuIcon.imageset/icon_36.png View File


BIN
DisplayBacklight/Assets.xcassets/MenuIcon.imageset/icon_54.png View File


+ 737
- 0
DisplayBacklight/Base.lproj/MainMenu.xib View File

1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9532" systemVersion="14F2109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
3
+    <dependencies>
4
+        <deployment identifier="macosx"/>
5
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9532"/>
6
+    </dependencies>
7
+    <objects>
8
+        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
9
+            <connections>
10
+                <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
11
+            </connections>
12
+        </customObject>
13
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
14
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
15
+        <customObject id="Voe-Tx-rLC" customClass="AppDelegate">
16
+            <connections>
17
+                <outlet property="application" destination="-3" id="3TG-JL-HaQ"/>
18
+                <outlet property="brightnessItem" destination="MYl-J7-okP" id="pON-9W-gMG"/>
19
+                <outlet property="brightnessLabel" destination="jtw-ZC-4zA" id="Inv-ur-QX5"/>
20
+                <outlet property="brightnessSlider" destination="axk-la-AqI" id="J4R-Mw-KN7"/>
21
+                <outlet property="buttonAmbilight" destination="u4m-52-C1A" id="RrD-UM-Vkj"/>
22
+                <outlet property="menuPorts" destination="lFU-D0-M5p" id="pHd-AA-S3J"/>
23
+                <outlet property="statusMenu" destination="g7M-LS-DgA" id="txO-T1-8yD"/>
24
+            </connections>
25
+        </customObject>
26
+        <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
27
+        <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
28
+            <items>
29
+                <menuItem title="DisplayBacklight" id="1Xt-HY-uBw">
30
+                    <modifierMask key="keyEquivalentModifierMask"/>
31
+                    <menu key="submenu" title="DisplayBacklight" systemMenu="apple" id="uQy-DD-JDr">
32
+                        <items>
33
+                            <menuItem title="About DisplayBacklight" id="5kV-Vb-QxS">
34
+                                <modifierMask key="keyEquivalentModifierMask"/>
35
+                                <connections>
36
+                                    <action selector="showAbout:" target="Voe-Tx-rLC" id="1ud-LX-A1L"/>
37
+                                </connections>
38
+                            </menuItem>
39
+                            <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
40
+                            <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
41
+                            <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
42
+                            <menuItem title="Services" id="NMo-om-nkz">
43
+                                <modifierMask key="keyEquivalentModifierMask"/>
44
+                                <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
45
+                            </menuItem>
46
+                            <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
47
+                            <menuItem title="Hide DisplayBacklight" keyEquivalent="h" id="Olw-nP-bQN">
48
+                                <connections>
49
+                                    <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
50
+                                </connections>
51
+                            </menuItem>
52
+                            <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
53
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
54
+                                <connections>
55
+                                    <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
56
+                                </connections>
57
+                            </menuItem>
58
+                            <menuItem title="Show All" id="Kd2-mp-pUS">
59
+                                <modifierMask key="keyEquivalentModifierMask"/>
60
+                                <connections>
61
+                                    <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
62
+                                </connections>
63
+                            </menuItem>
64
+                            <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
65
+                            <menuItem title="Quit DisplayBacklight" keyEquivalent="q" id="4sb-4s-VLi">
66
+                                <connections>
67
+                                    <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
68
+                                </connections>
69
+                            </menuItem>
70
+                        </items>
71
+                    </menu>
72
+                </menuItem>
73
+                <menuItem title="File" id="dMs-cI-mzQ">
74
+                    <modifierMask key="keyEquivalentModifierMask"/>
75
+                    <menu key="submenu" title="File" id="bib-Uj-vzu">
76
+                        <items>
77
+                            <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
78
+                                <connections>
79
+                                    <action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
80
+                                </connections>
81
+                            </menuItem>
82
+                            <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
83
+                                <connections>
84
+                                    <action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
85
+                                </connections>
86
+                            </menuItem>
87
+                            <menuItem title="Open Recent" id="tXI-mr-wws">
88
+                                <modifierMask key="keyEquivalentModifierMask"/>
89
+                                <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
90
+                                    <items>
91
+                                        <menuItem title="Clear Menu" id="vNY-rz-j42">
92
+                                            <modifierMask key="keyEquivalentModifierMask"/>
93
+                                            <connections>
94
+                                                <action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
95
+                                            </connections>
96
+                                        </menuItem>
97
+                                    </items>
98
+                                </menu>
99
+                            </menuItem>
100
+                            <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
101
+                            <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
102
+                                <connections>
103
+                                    <action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
104
+                                </connections>
105
+                            </menuItem>
106
+                            <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
107
+                                <connections>
108
+                                    <action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
109
+                                </connections>
110
+                            </menuItem>
111
+                            <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
112
+                                <connections>
113
+                                    <action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
114
+                                </connections>
115
+                            </menuItem>
116
+                            <menuItem title="Revert to Saved" id="KaW-ft-85H">
117
+                                <modifierMask key="keyEquivalentModifierMask"/>
118
+                                <connections>
119
+                                    <action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
120
+                                </connections>
121
+                            </menuItem>
122
+                            <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
123
+                            <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
124
+                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
125
+                                <connections>
126
+                                    <action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
127
+                                </connections>
128
+                            </menuItem>
129
+                            <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
130
+                                <connections>
131
+                                    <action selector="print:" target="-1" id="qaZ-4w-aoO"/>
132
+                                </connections>
133
+                            </menuItem>
134
+                        </items>
135
+                    </menu>
136
+                </menuItem>
137
+                <menuItem title="Edit" id="5QF-Oa-p0T">
138
+                    <modifierMask key="keyEquivalentModifierMask"/>
139
+                    <menu key="submenu" title="Edit" id="W48-6f-4Dl">
140
+                        <items>
141
+                            <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
142
+                                <connections>
143
+                                    <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
144
+                                </connections>
145
+                            </menuItem>
146
+                            <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
147
+                                <connections>
148
+                                    <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
149
+                                </connections>
150
+                            </menuItem>
151
+                            <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
152
+                            <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
153
+                                <connections>
154
+                                    <action selector="cut:" target="-1" id="YJe-68-I9s"/>
155
+                                </connections>
156
+                            </menuItem>
157
+                            <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
158
+                                <connections>
159
+                                    <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
160
+                                </connections>
161
+                            </menuItem>
162
+                            <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
163
+                                <connections>
164
+                                    <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
165
+                                </connections>
166
+                            </menuItem>
167
+                            <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
168
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
169
+                                <connections>
170
+                                    <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
171
+                                </connections>
172
+                            </menuItem>
173
+                            <menuItem title="Delete" id="pa3-QI-u2k">
174
+                                <modifierMask key="keyEquivalentModifierMask"/>
175
+                                <connections>
176
+                                    <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
177
+                                </connections>
178
+                            </menuItem>
179
+                            <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
180
+                                <connections>
181
+                                    <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
182
+                                </connections>
183
+                            </menuItem>
184
+                            <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
185
+                            <menuItem title="Find" id="4EN-yA-p0u">
186
+                                <modifierMask key="keyEquivalentModifierMask"/>
187
+                                <menu key="submenu" title="Find" id="1b7-l0-nxx">
188
+                                    <items>
189
+                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
190
+                                            <connections>
191
+                                                <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
192
+                                            </connections>
193
+                                        </menuItem>
194
+                                        <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
195
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
196
+                                            <connections>
197
+                                                <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
198
+                                            </connections>
199
+                                        </menuItem>
200
+                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
201
+                                            <connections>
202
+                                                <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
203
+                                            </connections>
204
+                                        </menuItem>
205
+                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
206
+                                            <connections>
207
+                                                <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
208
+                                            </connections>
209
+                                        </menuItem>
210
+                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
211
+                                            <connections>
212
+                                                <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
213
+                                            </connections>
214
+                                        </menuItem>
215
+                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
216
+                                            <connections>
217
+                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
218
+                                            </connections>
219
+                                        </menuItem>
220
+                                    </items>
221
+                                </menu>
222
+                            </menuItem>
223
+                            <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
224
+                                <modifierMask key="keyEquivalentModifierMask"/>
225
+                                <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
226
+                                    <items>
227
+                                        <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
228
+                                            <connections>
229
+                                                <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
230
+                                            </connections>
231
+                                        </menuItem>
232
+                                        <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
233
+                                            <connections>
234
+                                                <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
235
+                                            </connections>
236
+                                        </menuItem>
237
+                                        <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
238
+                                        <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
239
+                                            <modifierMask key="keyEquivalentModifierMask"/>
240
+                                            <connections>
241
+                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
242
+                                            </connections>
243
+                                        </menuItem>
244
+                                        <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
245
+                                            <modifierMask key="keyEquivalentModifierMask"/>
246
+                                            <connections>
247
+                                                <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
248
+                                            </connections>
249
+                                        </menuItem>
250
+                                        <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
251
+                                            <modifierMask key="keyEquivalentModifierMask"/>
252
+                                            <connections>
253
+                                                <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
254
+                                            </connections>
255
+                                        </menuItem>
256
+                                    </items>
257
+                                </menu>
258
+                            </menuItem>
259
+                            <menuItem title="Substitutions" id="9ic-FL-obx">
260
+                                <modifierMask key="keyEquivalentModifierMask"/>
261
+                                <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
262
+                                    <items>
263
+                                        <menuItem title="Show Substitutions" id="z6F-FW-3nz">
264
+                                            <modifierMask key="keyEquivalentModifierMask"/>
265
+                                            <connections>
266
+                                                <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
267
+                                            </connections>
268
+                                        </menuItem>
269
+                                        <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
270
+                                        <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
271
+                                            <modifierMask key="keyEquivalentModifierMask"/>
272
+                                            <connections>
273
+                                                <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
274
+                                            </connections>
275
+                                        </menuItem>
276
+                                        <menuItem title="Smart Quotes" id="hQb-2v-fYv">
277
+                                            <modifierMask key="keyEquivalentModifierMask"/>
278
+                                            <connections>
279
+                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
280
+                                            </connections>
281
+                                        </menuItem>
282
+                                        <menuItem title="Smart Dashes" id="rgM-f4-ycn">
283
+                                            <modifierMask key="keyEquivalentModifierMask"/>
284
+                                            <connections>
285
+                                                <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
286
+                                            </connections>
287
+                                        </menuItem>
288
+                                        <menuItem title="Smart Links" id="cwL-P1-jid">
289
+                                            <modifierMask key="keyEquivalentModifierMask"/>
290
+                                            <connections>
291
+                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
292
+                                            </connections>
293
+                                        </menuItem>
294
+                                        <menuItem title="Data Detectors" id="tRr-pd-1PS">
295
+                                            <modifierMask key="keyEquivalentModifierMask"/>
296
+                                            <connections>
297
+                                                <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
298
+                                            </connections>
299
+                                        </menuItem>
300
+                                        <menuItem title="Text Replacement" id="HFQ-gK-NFA">
301
+                                            <modifierMask key="keyEquivalentModifierMask"/>
302
+                                            <connections>
303
+                                                <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
304
+                                            </connections>
305
+                                        </menuItem>
306
+                                    </items>
307
+                                </menu>
308
+                            </menuItem>
309
+                            <menuItem title="Transformations" id="2oI-Rn-ZJC">
310
+                                <modifierMask key="keyEquivalentModifierMask"/>
311
+                                <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
312
+                                    <items>
313
+                                        <menuItem title="Make Upper Case" id="vmV-6d-7jI">
314
+                                            <modifierMask key="keyEquivalentModifierMask"/>
315
+                                            <connections>
316
+                                                <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
317
+                                            </connections>
318
+                                        </menuItem>
319
+                                        <menuItem title="Make Lower Case" id="d9M-CD-aMd">
320
+                                            <modifierMask key="keyEquivalentModifierMask"/>
321
+                                            <connections>
322
+                                                <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
323
+                                            </connections>
324
+                                        </menuItem>
325
+                                        <menuItem title="Capitalize" id="UEZ-Bs-lqG">
326
+                                            <modifierMask key="keyEquivalentModifierMask"/>
327
+                                            <connections>
328
+                                                <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
329
+                                            </connections>
330
+                                        </menuItem>
331
+                                    </items>
332
+                                </menu>
333
+                            </menuItem>
334
+                            <menuItem title="Speech" id="xrE-MZ-jX0">
335
+                                <modifierMask key="keyEquivalentModifierMask"/>
336
+                                <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
337
+                                    <items>
338
+                                        <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
339
+                                            <modifierMask key="keyEquivalentModifierMask"/>
340
+                                            <connections>
341
+                                                <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
342
+                                            </connections>
343
+                                        </menuItem>
344
+                                        <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
345
+                                            <modifierMask key="keyEquivalentModifierMask"/>
346
+                                            <connections>
347
+                                                <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
348
+                                            </connections>
349
+                                        </menuItem>
350
+                                    </items>
351
+                                </menu>
352
+                            </menuItem>
353
+                        </items>
354
+                    </menu>
355
+                </menuItem>
356
+                <menuItem title="Format" id="jxT-CU-nIS">
357
+                    <modifierMask key="keyEquivalentModifierMask"/>
358
+                    <menu key="submenu" title="Format" id="GEO-Iw-cKr">
359
+                        <items>
360
+                            <menuItem title="Font" id="Gi5-1S-RQB">
361
+                                <modifierMask key="keyEquivalentModifierMask"/>
362
+                                <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
363
+                                    <items>
364
+                                        <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
365
+                                            <connections>
366
+                                                <action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
367
+                                            </connections>
368
+                                        </menuItem>
369
+                                        <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
370
+                                            <connections>
371
+                                                <action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
372
+                                            </connections>
373
+                                        </menuItem>
374
+                                        <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
375
+                                            <connections>
376
+                                                <action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
377
+                                            </connections>
378
+                                        </menuItem>
379
+                                        <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
380
+                                            <connections>
381
+                                                <action selector="underline:" target="-1" id="FYS-2b-JAY"/>
382
+                                            </connections>
383
+                                        </menuItem>
384
+                                        <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
385
+                                        <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
386
+                                            <connections>
387
+                                                <action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
388
+                                            </connections>
389
+                                        </menuItem>
390
+                                        <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
391
+                                            <connections>
392
+                                                <action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
393
+                                            </connections>
394
+                                        </menuItem>
395
+                                        <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
396
+                                        <menuItem title="Kern" id="jBQ-r6-VK2">
397
+                                            <modifierMask key="keyEquivalentModifierMask"/>
398
+                                            <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
399
+                                                <items>
400
+                                                    <menuItem title="Use Default" id="GUa-eO-cwY">
401
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
402
+                                                        <connections>
403
+                                                            <action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
404
+                                                        </connections>
405
+                                                    </menuItem>
406
+                                                    <menuItem title="Use None" id="cDB-IK-hbR">
407
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
408
+                                                        <connections>
409
+                                                            <action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
410
+                                                        </connections>
411
+                                                    </menuItem>
412
+                                                    <menuItem title="Tighten" id="46P-cB-AYj">
413
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
414
+                                                        <connections>
415
+                                                            <action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
416
+                                                        </connections>
417
+                                                    </menuItem>
418
+                                                    <menuItem title="Loosen" id="ogc-rX-tC1">
419
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
420
+                                                        <connections>
421
+                                                            <action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
422
+                                                        </connections>
423
+                                                    </menuItem>
424
+                                                </items>
425
+                                            </menu>
426
+                                        </menuItem>
427
+                                        <menuItem title="Ligatures" id="o6e-r0-MWq">
428
+                                            <modifierMask key="keyEquivalentModifierMask"/>
429
+                                            <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
430
+                                                <items>
431
+                                                    <menuItem title="Use Default" id="agt-UL-0e3">
432
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
433
+                                                        <connections>
434
+                                                            <action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
435
+                                                        </connections>
436
+                                                    </menuItem>
437
+                                                    <menuItem title="Use None" id="J7y-lM-qPV">
438
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
439
+                                                        <connections>
440
+                                                            <action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
441
+                                                        </connections>
442
+                                                    </menuItem>
443
+                                                    <menuItem title="Use All" id="xQD-1f-W4t">
444
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
445
+                                                        <connections>
446
+                                                            <action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
447
+                                                        </connections>
448
+                                                    </menuItem>
449
+                                                </items>
450
+                                            </menu>
451
+                                        </menuItem>
452
+                                        <menuItem title="Baseline" id="OaQ-X3-Vso">
453
+                                            <modifierMask key="keyEquivalentModifierMask"/>
454
+                                            <menu key="submenu" title="Baseline" id="ijk-EB-dga">
455
+                                                <items>
456
+                                                    <menuItem title="Use Default" id="3Om-Ey-2VK">
457
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
458
+                                                        <connections>
459
+                                                            <action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
460
+                                                        </connections>
461
+                                                    </menuItem>
462
+                                                    <menuItem title="Superscript" id="Rqc-34-cIF">
463
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
464
+                                                        <connections>
465
+                                                            <action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
466
+                                                        </connections>
467
+                                                    </menuItem>
468
+                                                    <menuItem title="Subscript" id="I0S-gh-46l">
469
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
470
+                                                        <connections>
471
+                                                            <action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
472
+                                                        </connections>
473
+                                                    </menuItem>
474
+                                                    <menuItem title="Raise" id="2h7-ER-AoG">
475
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
476
+                                                        <connections>
477
+                                                            <action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
478
+                                                        </connections>
479
+                                                    </menuItem>
480
+                                                    <menuItem title="Lower" id="1tx-W0-xDw">
481
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
482
+                                                        <connections>
483
+                                                            <action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
484
+                                                        </connections>
485
+                                                    </menuItem>
486
+                                                </items>
487
+                                            </menu>
488
+                                        </menuItem>
489
+                                        <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
490
+                                        <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
491
+                                            <connections>
492
+                                                <action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
493
+                                            </connections>
494
+                                        </menuItem>
495
+                                        <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
496
+                                        <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
497
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
498
+                                            <connections>
499
+                                                <action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
500
+                                            </connections>
501
+                                        </menuItem>
502
+                                        <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
503
+                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
504
+                                            <connections>
505
+                                                <action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
506
+                                            </connections>
507
+                                        </menuItem>
508
+                                    </items>
509
+                                </menu>
510
+                            </menuItem>
511
+                            <menuItem title="Text" id="Fal-I4-PZk">
512
+                                <modifierMask key="keyEquivalentModifierMask"/>
513
+                                <menu key="submenu" title="Text" id="d9c-me-L2H">
514
+                                    <items>
515
+                                        <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
516
+                                            <connections>
517
+                                                <action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
518
+                                            </connections>
519
+                                        </menuItem>
520
+                                        <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
521
+                                            <connections>
522
+                                                <action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
523
+                                            </connections>
524
+                                        </menuItem>
525
+                                        <menuItem title="Justify" id="J5U-5w-g23">
526
+                                            <modifierMask key="keyEquivalentModifierMask"/>
527
+                                            <connections>
528
+                                                <action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
529
+                                            </connections>
530
+                                        </menuItem>
531
+                                        <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
532
+                                            <connections>
533
+                                                <action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
534
+                                            </connections>
535
+                                        </menuItem>
536
+                                        <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
537
+                                        <menuItem title="Writing Direction" id="H1b-Si-o9J">
538
+                                            <modifierMask key="keyEquivalentModifierMask"/>
539
+                                            <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
540
+                                                <items>
541
+                                                    <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
542
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
543
+                                                    </menuItem>
544
+                                                    <menuItem id="YGs-j5-SAR">
545
+                                                        <string key="title">	Default</string>
546
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
547
+                                                        <connections>
548
+                                                            <action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
549
+                                                        </connections>
550
+                                                    </menuItem>
551
+                                                    <menuItem id="Lbh-J2-qVU">
552
+                                                        <string key="title">	Left to Right</string>
553
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
554
+                                                        <connections>
555
+                                                            <action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
556
+                                                        </connections>
557
+                                                    </menuItem>
558
+                                                    <menuItem id="jFq-tB-4Kx">
559
+                                                        <string key="title">	Right to Left</string>
560
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
561
+                                                        <connections>
562
+                                                            <action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
563
+                                                        </connections>
564
+                                                    </menuItem>
565
+                                                    <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
566
+                                                    <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
567
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
568
+                                                    </menuItem>
569
+                                                    <menuItem id="Nop-cj-93Q">
570
+                                                        <string key="title">	Default</string>
571
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
572
+                                                        <connections>
573
+                                                            <action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
574
+                                                        </connections>
575
+                                                    </menuItem>
576
+                                                    <menuItem id="BgM-ve-c93">
577
+                                                        <string key="title">	Left to Right</string>
578
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
579
+                                                        <connections>
580
+                                                            <action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
581
+                                                        </connections>
582
+                                                    </menuItem>
583
+                                                    <menuItem id="RB4-Sm-HuC">
584
+                                                        <string key="title">	Right to Left</string>
585
+                                                        <modifierMask key="keyEquivalentModifierMask"/>
586
+                                                        <connections>
587
+                                                            <action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
588
+                                                        </connections>
589
+                                                    </menuItem>
590
+                                                </items>
591
+                                            </menu>
592
+                                        </menuItem>
593
+                                        <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
594
+                                        <menuItem title="Show Ruler" id="vLm-3I-IUL">
595
+                                            <modifierMask key="keyEquivalentModifierMask"/>
596
+                                            <connections>
597
+                                                <action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
598
+                                            </connections>
599
+                                        </menuItem>
600
+                                        <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
601
+                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
602
+                                            <connections>
603
+                                                <action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
604
+                                            </connections>
605
+                                        </menuItem>
606
+                                        <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
607
+                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
608
+                                            <connections>
609
+                                                <action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
610
+                                            </connections>
611
+                                        </menuItem>
612
+                                    </items>
613
+                                </menu>
614
+                            </menuItem>
615
+                        </items>
616
+                    </menu>
617
+                </menuItem>
618
+                <menuItem title="View" id="H8h-7b-M4v">
619
+                    <modifierMask key="keyEquivalentModifierMask"/>
620
+                    <menu key="submenu" title="View" id="HyV-fh-RgO">
621
+                        <items>
622
+                            <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
623
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
624
+                                <connections>
625
+                                    <action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
626
+                                </connections>
627
+                            </menuItem>
628
+                            <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
629
+                                <modifierMask key="keyEquivalentModifierMask"/>
630
+                                <connections>
631
+                                    <action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
632
+                                </connections>
633
+                            </menuItem>
634
+                        </items>
635
+                    </menu>
636
+                </menuItem>
637
+                <menuItem title="Window" id="aUF-d1-5bR">
638
+                    <modifierMask key="keyEquivalentModifierMask"/>
639
+                    <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
640
+                        <items>
641
+                            <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
642
+                                <connections>
643
+                                    <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
644
+                                </connections>
645
+                            </menuItem>
646
+                            <menuItem title="Zoom" id="R4o-n2-Eq4">
647
+                                <modifierMask key="keyEquivalentModifierMask"/>
648
+                                <connections>
649
+                                    <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
650
+                                </connections>
651
+                            </menuItem>
652
+                            <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
653
+                            <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
654
+                                <modifierMask key="keyEquivalentModifierMask"/>
655
+                                <connections>
656
+                                    <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
657
+                                </connections>
658
+                            </menuItem>
659
+                        </items>
660
+                    </menu>
661
+                </menuItem>
662
+                <menuItem title="Help" id="wpr-3q-Mcd">
663
+                    <modifierMask key="keyEquivalentModifierMask"/>
664
+                    <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
665
+                        <items>
666
+                            <menuItem title="DisplayBacklight Help" keyEquivalent="?" id="FKE-Sm-Kum">
667
+                                <connections>
668
+                                    <action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
669
+                                </connections>
670
+                            </menuItem>
671
+                        </items>
672
+                    </menu>
673
+                </menuItem>
674
+            </items>
675
+        </menu>
676
+        <menu id="g7M-LS-DgA">
677
+            <items>
678
+                <menuItem title="Ambilight" keyEquivalent="w" id="u4m-52-C1A">
679
+                    <connections>
680
+                        <action selector="toggleAmbilight:" target="Voe-Tx-rLC" id="HwY-hB-Ok2"/>
681
+                    </connections>
682
+                </menuItem>
683
+                <menuItem title="Brightness" id="Oh0-1U-yDg">
684
+                    <modifierMask key="keyEquivalentModifierMask"/>
685
+                    <menu key="submenu" title="Brightness" id="jRu-sM-3xB">
686
+                        <items>
687
+                            <menuItem title="Item" id="MYl-J7-okP">
688
+                                <modifierMask key="keyEquivalentModifierMask"/>
689
+                            </menuItem>
690
+                            <menuItem title="Value: 50%" enabled="NO" id="jtw-ZC-4zA">
691
+                                <modifierMask key="keyEquivalentModifierMask"/>
692
+                            </menuItem>
693
+                        </items>
694
+                    </menu>
695
+                </menuItem>
696
+                <menuItem isSeparatorItem="YES" id="Bgw-0M-KDy"/>
697
+                <menuItem title="Serial Port" id="K0K-po-BF0">
698
+                    <modifierMask key="keyEquivalentModifierMask"/>
699
+                    <menu key="submenu" title="Serial Port" id="lFU-D0-M5p">
700
+                        <items>
701
+                            <menuItem title="No Serial Ports detected!" enabled="NO" id="vUg-b8-DJP">
702
+                                <modifierMask key="keyEquivalentModifierMask"/>
703
+                            </menuItem>
704
+                        </items>
705
+                    </menu>
706
+                </menuItem>
707
+                <menuItem title="Relist Devices" id="jVd-gl-Z6F">
708
+                    <modifierMask key="keyEquivalentModifierMask"/>
709
+                    <connections>
710
+                        <action selector="relistSerialPorts:" target="Voe-Tx-rLC" id="iFU-PL-xR8"/>
711
+                    </connections>
712
+                </menuItem>
713
+                <menuItem title="About DisplayBacklight" id="GeK-ey-XEn">
714
+                    <modifierMask key="keyEquivalentModifierMask"/>
715
+                    <connections>
716
+                        <action selector="showAbout:" target="Voe-Tx-rLC" id="Odh-zd-kmk"/>
717
+                    </connections>
718
+                </menuItem>
719
+                <menuItem title="Quit DisplayBacklight" keyEquivalent="q" id="2cH-5V-zpa">
720
+                    <connections>
721
+                        <action selector="terminate:" target="-1" id="JHW-Uu-Xhz"/>
722
+                    </connections>
723
+                </menuItem>
724
+            </items>
725
+            <point key="canvasLocation" x="368" y="324.5"/>
726
+        </menu>
727
+        <slider verticalHuggingPriority="750" id="axk-la-AqI">
728
+            <rect key="frame" x="0.0" y="0.0" width="129" height="30"/>
729
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
730
+            <sliderCell key="cell" alignment="left" maxValue="100" doubleValue="50" tickMarkPosition="below" numberOfTickMarks="11" sliderType="linear" id="IRQ-g4-OAA"/>
731
+            <connections>
732
+                <action selector="brightnessMoved:" target="Voe-Tx-rLC" id="Wxj-Xc-ZtT"/>
733
+            </connections>
734
+            <point key="canvasLocation" x="122.5" y="326.5"/>
735
+        </slider>
736
+    </objects>
737
+</document>

+ 38
- 0
DisplayBacklight/Info.plist View File

1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>CFBundleDevelopmentRegion</key>
6
+	<string>en</string>
7
+	<key>CFBundleExecutable</key>
8
+	<string>$(EXECUTABLE_NAME)</string>
9
+	<key>CFBundleIconFile</key>
10
+	<string></string>
11
+	<key>CFBundleIdentifier</key>
12
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
13
+	<key>CFBundleInfoDictionaryVersion</key>
14
+	<string>6.0</string>
15
+	<key>CFBundleName</key>
16
+	<string>$(PRODUCT_NAME)</string>
17
+	<key>CFBundlePackageType</key>
18
+	<string>APPL</string>
19
+	<key>CFBundleShortVersionString</key>
20
+	<string>1.0</string>
21
+	<key>CFBundleSignature</key>
22
+	<string>????</string>
23
+	<key>CFBundleVersion</key>
24
+	<string>53</string>
25
+	<key>LSApplicationCategoryType</key>
26
+	<string>public.app-category.utilities</string>
27
+	<key>LSMinimumSystemVersion</key>
28
+	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
29
+	<key>LSUIElement</key>
30
+	<true/>
31
+	<key>NSHumanReadableCopyright</key>
32
+	<string>Copyright © 2016 xythobuz. All rights reserved.</string>
33
+	<key>NSMainNibFile</key>
34
+	<string>MainMenu</string>
35
+	<key>NSPrincipalClass</key>
36
+	<string>NSApplication</string>
37
+</dict>
38
+</plist>

+ 23
- 0
DisplayBacklight/Screenshot.h View File

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

+ 144
- 0
DisplayBacklight/Screenshot.m View File

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

+ 24
- 0
DisplayBacklight/Serial.h View File

1
+//
2
+//  Serial.h
3
+//  DisplayBacklight
4
+//
5
+//  Created by Thomas Buck on 14.12.15.
6
+//  Copyright © 2015 xythobuz. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+
11
+@interface Serial : NSObject
12
+
13
+@property (strong) NSString *portName;
14
+
15
+- (NSInteger)openPort;
16
+- (void)closePort;
17
+- (BOOL)isOpen;
18
+- (BOOL)hasData;
19
+- (void)sendData:(const char *)data withLength:(size_t)length;
20
+- (void)sendString:(NSString *)string;
21
+
22
++ (NSArray *)listSerialPorts;
23
+
24
+@end

+ 309
- 0
DisplayBacklight/Serial.m View File

1
+//
2
+//  Serial.m
3
+//  DisplayBacklight
4
+//
5
+//  For more informations refer to this document:
6
+//  https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/SerialDevices.html
7
+//
8
+//  Created by Thomas Buck on 14.12.15.
9
+//  Copyright © 2015 xythobuz. All rights reserved.
10
+//
11
+
12
+//#define DEBUG_TEXT
13
+//#define DEBUG_DATA
14
+
15
+#import <Cocoa/Cocoa.h>
16
+#import <IOKit/IOKitLib.h>
17
+#import <IOKit/serial/IOSerialKeys.h>
18
+
19
+#import <termios.h>
20
+#import <fcntl.h>
21
+#import <unistd.h>
22
+#import <poll.h>
23
+#import <sys/ioctl.h>
24
+
25
+#import "Serial.h"
26
+
27
+#define MAX_SEND_ERRORS 10
28
+
29
+@interface Serial ()
30
+
31
+@property (assign) int fd;
32
+
33
++ (kern_return_t)findSerialPorts:(io_iterator_t *)matches;
34
++ (kern_return_t)getSerialPortPath:(io_iterator_t)serialPortIterator to:(char **)deviceFilePath with:(CFIndex)maxPathCount and:(CFIndex)maxPathSize;
35
+
36
+@end
37
+
38
+@implementation Serial
39
+
40
+@synthesize fd, portName;
41
+
42
+- (id)init {
43
+    self = [super init];
44
+    if (self != nil) {
45
+        fd = -1;
46
+        portName = nil;
47
+    }
48
+    return self;
49
+}
50
+
51
+- (NSInteger)openPort {
52
+    // We need a port name
53
+    if (portName == nil) {
54
+        NSLog(@"Can't open serial port without name!\n");
55
+        return 1;
56
+    }
57
+    
58
+    // Check if there was already a port opened
59
+    if (fd > -1) {
60
+        NSLog(@"Closing previously opened serial port \"%@\"!\n", portName);
61
+        close(fd);
62
+    }
63
+    
64
+#ifdef DEBUG
65
+    NSLog(@"Opening serial port \"%@\"...\n", portName);
66
+#endif
67
+    
68
+    // Open port read-only, without controlling terminal, non-blocking
69
+    fd = open([portName UTF8String], O_RDWR | O_NOCTTY | O_NONBLOCK);
70
+    if (fd == -1) {
71
+        NSLog(@"Error opening serial port \"%@\": %s (%d)!\n", portName, strerror(errno), errno);
72
+        return 1;
73
+    }
74
+    
75
+    // Prevent additional opens except by root-owned processes
76
+    if (ioctl(fd, TIOCEXCL) == -1) {
77
+        NSLog(@"Error enabling exclusive access on \"%@\": %s (%d)!\n", portName, strerror(errno), errno);
78
+        return 1;
79
+    }
80
+    
81
+    fcntl(fd, F_SETFL, 0); // Enable blocking I/O
82
+    
83
+    // Read current settings
84
+    struct termios options;
85
+    tcgetattr(fd, &options);
86
+    
87
+    // Clear all settings
88
+    options.c_lflag = 0;
89
+    options.c_oflag = 0;
90
+    options.c_iflag = 0;
91
+    options.c_cflag = 0;
92
+    
93
+    options.c_cflag |= CS8; // 8 data bits
94
+    options.c_cflag |= CREAD; // Enable receiver
95
+    options.c_cflag |= CLOCAL; // Ignore modem status lines
96
+    
97
+    // Set Baudrate
98
+    cfsetispeed(&options, B115200);
99
+    cfsetospeed(&options, B115200);
100
+    
101
+    options.c_cc[VMIN] = 0; // Return even with zero bytes...
102
+    options.c_cc[VTIME] = 1; // ...but only after .1 seconds
103
+    
104
+    // Set new settings
105
+    tcsetattr(fd, TCSANOW, &options);
106
+    tcflush(fd, TCIOFLUSH);
107
+    
108
+    return 0;
109
+}
110
+
111
+- (void)closePort {
112
+#ifdef DEBUG
113
+    NSLog(@"Closing serial port \"%@\"...\n", portName);
114
+#endif
115
+    
116
+    if (fd > -1) {
117
+        close(fd);
118
+    } else {
119
+        NSLog(@"Trying to close already closed port!\n");
120
+    }
121
+    fd = -1;
122
+}
123
+
124
+- (BOOL)isOpen {
125
+    if (fd > -1) {
126
+        return YES;
127
+    } else {
128
+        return NO;
129
+    }
130
+}
131
+
132
+- (BOOL)hasData {
133
+    if (fd < 0) {
134
+        NSLog(@"Error trying to poll a closed port!\n");
135
+        return NO;
136
+    }
137
+    
138
+    struct pollfd fds;
139
+    fds.fd = fd;
140
+    fds.events = (POLLIN | POLLPRI); // Data may be read
141
+    int val = poll(&fds, 1, 0);
142
+    if (val > 0) {
143
+        return YES;
144
+    } else if (val == 0) {
145
+        return NO;
146
+    } else {
147
+        NSLog(@"Error polling serial port: %s (%d)!\n", strerror(errno), errno);
148
+        return NO;
149
+    }
150
+}
151
+
152
+- (void)sendString:(NSString *)string {
153
+    if (fd < 0) {
154
+        NSLog(@"Error trying to send to a closed port!\n");
155
+        return;
156
+    }
157
+    
158
+    const char *data = [string UTF8String];
159
+    size_t length = strlen(data);
160
+    
161
+#ifdef DEBUG_TEXT
162
+    NSLog(@"Sending string %s", data);
163
+#endif
164
+    
165
+    [self sendData:data withLength:length];
166
+}
167
+
168
+- (void)sendData:(const char *)data withLength:(size_t)length {
169
+#ifdef DEBUG_DATA
170
+    NSLog(@"Sending binary-data with %zu bytes.", length);
171
+#endif
172
+
173
+    int errorCount = 0;
174
+    ssize_t sent = 0;
175
+    while (sent < length) {
176
+        ssize_t ret = write(fd, data + sent, length - sent);
177
+        if (ret < 0) {
178
+            NSLog(@"Error writing to serial port: %s (%d)!\n", strerror(errno), errno);
179
+            errorCount++;
180
+            if (errorCount >= MAX_SEND_ERRORS) {
181
+#ifdef DEBUG
182
+                NSLog(@"Too many send errors! Giving up...\n");
183
+#endif
184
+                return;
185
+            }
186
+        } else {
187
+            sent += ret;
188
+        }
189
+    }
190
+}
191
+
192
++ (NSArray *)listSerialPorts {
193
+    // Get Iterator with all serial ports
194
+    io_iterator_t serialPortIterator;
195
+    kern_return_t kernResult = [Serial findSerialPorts:&serialPortIterator];
196
+    
197
+    // Create 2D array
198
+    char **portList;
199
+    portList = malloc(100 * sizeof(char *));
200
+    for (int i = 0; i < 100; i++) portList[i] = malloc(200 * sizeof(char));
201
+    
202
+    // Copy device name into C-String array
203
+    kernResult = [Serial getSerialPortPath:serialPortIterator to:portList with:100 and:200];
204
+    IOObjectRelease(serialPortIterator);
205
+    
206
+    // Copy contents into NSString Array
207
+    NSString *stringList[100];
208
+    NSUInteger realCount = 0;
209
+    while (portList[realCount] != NULL) {
210
+        stringList[realCount] = [NSString stringWithCString:portList[realCount] encoding:NSUTF8StringEncoding];
211
+        realCount++;
212
+    }
213
+    
214
+    // Destroy 2D array
215
+    for (int i = 0; i < 100; i++) free(portList[i]);
216
+    free(portList);
217
+    
218
+    // And return them as NSArray
219
+    return [[NSArray alloc] initWithObjects:stringList count:realCount];
220
+}
221
+
222
++ (kern_return_t)findSerialPorts:(io_iterator_t *)matches {
223
+    kern_return_t kernResult;
224
+    mach_port_t masterPort;
225
+    CFMutableDictionaryRef classesToMatch;
226
+    
227
+    kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
228
+    if (KERN_SUCCESS != kernResult) {
229
+        NSLog(@"IOMasterPort returned %d\n", kernResult);
230
+        return kernResult;
231
+    }
232
+    
233
+    // Serial devices are instances of class IOSerialBSDClient.
234
+    classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
235
+    if (classesToMatch == NULL) {
236
+        NSLog(@"IOServiceMatching returned a NULL dictionary.\n");
237
+    } else {
238
+        CFDictionarySetValue(classesToMatch,
239
+                             CFSTR(kIOSerialBSDTypeKey),
240
+                             CFSTR(kIOSerialBSDAllTypes));
241
+        
242
+        // Each serial device object has a property with key
243
+        // kIOSerialBSDTypeKey and a value that is one of
244
+        // kIOSerialBSDAllTypes, kIOSerialBSDModemType,
245
+        // or kIOSerialBSDRS232Type. You can change the
246
+        // matching dictionary to find other types of serial
247
+        // devices by changing the last parameter in the above call
248
+        // to CFDictionarySetValue.
249
+    }
250
+    
251
+    kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch, matches);
252
+    if (KERN_SUCCESS != kernResult) {
253
+        NSLog(@"IOServiceGetMatchingServices returned %d\n", kernResult);
254
+        return kernResult;
255
+    }
256
+    
257
+    return kernResult;
258
+}
259
+
260
++ (kern_return_t)getSerialPortPath:(io_iterator_t)serialPortIterator to:(char **)deviceFilePath with:(CFIndex)maxPathCount and:(CFIndex)maxPathSize {
261
+    io_object_t modemService;
262
+    kern_return_t kernResult = KERN_FAILURE;
263
+    CFIndex i = 0;
264
+    
265
+    while ((modemService = IOIteratorNext(serialPortIterator)) && (i < (maxPathCount - 1))) {
266
+        CFTypeRef   deviceFilePathAsCFString;
267
+        
268
+        // Get the callout device's path (/dev/cu.xxxxx).
269
+        // The callout device should almost always be
270
+        // used. You would use the dialin device (/dev/tty.xxxxx) when
271
+        // monitoring a serial port for
272
+        // incoming calls, for example, a fax listener.
273
+        
274
+        deviceFilePathAsCFString = IORegistryEntryCreateCFProperty(modemService,
275
+                                                                   CFSTR(kIOCalloutDeviceKey),
276
+                                                                   kCFAllocatorDefault,
277
+                                                                   0);
278
+        if (deviceFilePathAsCFString) {
279
+            Boolean result;
280
+            
281
+            deviceFilePath[i][0] = '\0';
282
+            
283
+            // Convert the path from a CFString to a NULL-terminated C string
284
+            // for use with the POSIX open() call.
285
+            
286
+            result = CFStringGetCString(deviceFilePathAsCFString,
287
+                                        deviceFilePath[i],
288
+                                        maxPathSize,
289
+                                        kCFStringEncodingASCII);
290
+            CFRelease(deviceFilePathAsCFString);
291
+            
292
+            if (result) {
293
+                //NSLog(@"BSD path: %s\n", deviceFilePath[i]);
294
+                i++;
295
+                kernResult = KERN_SUCCESS;
296
+            }
297
+        }
298
+        
299
+        // Release the io_service_t now that we are done with it.
300
+        
301
+        (void) IOObjectRelease(modemService);
302
+    }
303
+    
304
+    deviceFilePath[i] = NULL;
305
+    
306
+    return kernResult;
307
+}
308
+
309
+@end

+ 13
- 0
DisplayBacklight/main.m View File

1
+//
2
+//  main.m
3
+//  CaseLights
4
+//
5
+//  Created by Thomas Buck on 21.12.15.
6
+//  Copyright © 2015 xythobuz. All rights reserved.
7
+//
8
+
9
+#import <Cocoa/Cocoa.h>
10
+
11
+int main(int argc, const char * argv[]) {
12
+    return NSApplicationMain(argc, argv);
13
+}

+ 138
- 0
LED-Strand-Test.ino View File

1
+/*
2
+ * Adafruit NeoPixel RGB-LED strand UART controller
3
+ * 
4
+ * This simple sketch controls an RGB LED strand via data received over UART.
5
+ * The protocol works as follows:
6
+ * 
7
+ * A single 'frame' that can be displayed, on our N LED strand, is defined as:
8
+ *     uint8_t data[N * 3] = { r0, g0, b0, r1, g1, b1, r2, g2, b2, ... };
9
+ * With consecutive 24-bit RGB pixels.
10
+ * This is simply transferred, as is, from the PC to the Arduino. To distinguish
11
+ * between start and end of different frames, a magic string is used, defined below.
12
+ * Hopefully it won't appear in the data stream... :)
13
+ */
14
+
15
+#include <Adafruit_NeoPixel.h>
16
+#ifdef __AVR__
17
+  #include <avr/power.h>
18
+#endif
19
+
20
+#define DEBUG
21
+
22
+#ifdef DEBUG
23
+#define debugPrint(x) Serial.print(x)
24
+#define debugPrintln(x) Serial.println(x)
25
+#else
26
+#define debugPrint(x)
27
+#define debugPrintln(x)
28
+#endif
29
+
30
+#define LED_PIN 2
31
+#define LED_COUNT 156
32
+#define BAUDRATE 115200
33
+#define MAGIC "xythobuzRGBled"
34
+
35
+// Optional auto-turn-off after no data received
36
+//#define TURN_OFF_TIMEOUT 60000l // in ms
37
+
38
+Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
39
+
40
+void setup() {
41
+    Serial.begin(BAUDRATE);
42
+    
43
+    strip.begin();
44
+    strip.show(); // Initialize all pixels to 'off'
45
+
46
+    debugPrintln("RGB LED controller ready!");
47
+    debugPrint("Config: ");
48
+    debugPrint(LED_COUNT);
49
+    debugPrintln(" LEDs...");
50
+    debugPrint("Magic-Word: \"");
51
+    debugPrint(MAGIC);
52
+    debugPrintln("\"");
53
+
54
+    // Blink LED as init message
55
+    strip.setPixelColor(0, 0x20, 0x20, 0x20);
56
+    strip.show();
57
+    delay(100);
58
+    strip.setPixelColor(0, 0x00, 0x00, 0x00);
59
+    strip.show();
60
+}
61
+
62
+#define WAIT_FOR_START 0
63
+#define RECEIVING_START 1
64
+#define RECEIVING_DATA 2
65
+ 
66
+uint8_t state = WAIT_FOR_START;
67
+uint8_t startPos = 0;
68
+uint16_t dataPos = 0;
69
+uint8_t frame[LED_COUNT * 3];
70
+
71
+#ifdef TURN_OFF_TIMEOUT
72
+unsigned long lastTime = millis();
73
+#endif
74
+
75
+static void setNewFrame() {
76
+    for (uint16_t i = 0; i < LED_COUNT; i++) {
77
+        strip.setPixelColor(i, frame[i * 3], frame[(i * 3) + 1], frame[(i * 3) + 2]);
78
+    }
79
+    strip.show();
80
+}
81
+
82
+void loop() {
83
+    if (!Serial.available()) {
84
+#ifdef TURN_OFF_TIMEOUT
85
+        if ((millis() - lastTime) >= TURN_OFF_TIMEOUT) {
86
+            for (int i = 0; i < (LED_COUNT * 3); i++) {
87
+                frame[i] = 0;
88
+            }
89
+            setNewFrame();
90
+        }
91
+#endif
92
+        return;
93
+    }
94
+
95
+#ifdef TURN_OFF_TIMEOUT
96
+    lastTime = millis();
97
+#endif
98
+
99
+    uint8_t c;
100
+    Serial.readBytes(&c, 1);
101
+    
102
+    if (state == WAIT_FOR_START) {
103
+        if (c == MAGIC[0]) {
104
+            state = RECEIVING_START;
105
+            startPos = 1;
106
+            debugPrintln("f");
107
+        } else {
108
+            debugPrintln("");
109
+        }
110
+    } else if (state == RECEIVING_START) {
111
+        if (startPos < strlen(MAGIC)) {
112
+            if (c == MAGIC[startPos]) {
113
+                startPos++;
114
+                debugPrintln("g");
115
+                if (startPos >= strlen(MAGIC)) {
116
+                    state = RECEIVING_DATA;
117
+                    dataPos = 0;
118
+                    debugPrintln("w");
119
+                }
120
+            } else {
121
+                state = WAIT_FOR_START;
122
+                debugPrintln("x");
123
+            }
124
+        } else {
125
+            // Should not happen
126
+            state = RECEIVING_START;
127
+            debugPrintln("s");
128
+        }
129
+    } else if (state == RECEIVING_DATA) {
130
+        frame[dataPos++] = c;
131
+        if (dataPos >= (LED_COUNT * 3)) {
132
+            debugPrintln("d");
133
+            setNewFrame();
134
+            state = WAIT_FOR_START;
135
+        }
136
+    }
137
+}
138
+

+ 43
- 0
test.sh View File

1
+#!/bin/bash
2
+
3
+# The serialHelper115200 file is my own SerialHelper Utility compiled
4
+# with 115200 baud. Get it from here:
5
+# https://github.com/xythobuz/SerialHelper
6
+
7
+DATA_FILE=led_data_file
8
+
9
+if [ $# -ne 3 ]; then
10
+    echo "Usage: three hex bytes for color"
11
+    echo "$0 rr gg bb"
12
+    exit
13
+fi
14
+
15
+PORTFILE=`ls /dev/tty.wchusbserial* | head -n 1`
16
+echo "Using $PORTFILE as serial port..."
17
+
18
+echo "Preparing data to send..."
19
+echo -n "xythobuzRGBled" > $DATA_FILE
20
+for i in {1..156}; do
21
+    echo -n -e "\x$1\x$2\x$3" >> $DATA_FILE
22
+done
23
+
24
+echo "Opening serial port..."
25
+serialHelper115200 -rw $PORTFILE >/dev/null 2>/dev/null &
26
+TERM_PID=$!
27
+echo "PID is $TERM_PID"
28
+
29
+echo "Waiting for Arduino to be ready..."
30
+sleep 3
31
+
32
+echo "Sending data..."
33
+cat $DATA_FILE > $PORTFILE
34
+
35
+echo "Waiting for data to appear..."
36
+sleep 1
37
+
38
+echo "Closing serial port..."
39
+kill $TERM_PID
40
+
41
+echo "Deleting created data file..."
42
+rm -rf $DATA_FILE
43
+

Loading…
Cancel
Save