Categories
AI Fun Linux Nerd Sniped Projects

Snooping USB with GPT

I mentioned in my previous post about the Juicy Crumb DockLite G4 that there were two shortcomings with the product:

  1. Their software to control the brightness of the display was macOS only.
  2. The display doesn’t support sleep, so you’re required to press a physical button to turn off the backlight.

Thanks to some USB packet snooping and GPT, both of these problems were solved quickly. 😎

I wanted Linux software control

What started this exploration was that I wanted to control the display from Linux because it’s currently connected to a Raspberry Pi, not a Mac.

Step 0: Check for an open source repo

Negative. I didn’t spot anything, so it’ll take a little more grunt work. I did run their helper app through a disassembler and spotted functions like getting and setting the backlight via IOKit which were all expected. It wasn’t necessary to go this far, it just confirms that we have some USB packet snooping to do.

Step 1: Get lsusb output

Running lsusb with the display connected to the RPi showed:

Bus 001 Device 009: ID 4a43:1d8a Juicy Crumb Systems DockLite

So I grabbed the output of lsusb -v -d 4a43:1d8a to feed it to the LLM later to provide more details about its USB specs.

Step 2: Capture USB packets on the Mac

The double-edged sword of Apple’s security is that it introduces all sorts of challenges for devs who need to get to lower level functionality, like capturing raw data from interfaces.

To successfully USB capture packets on a Mac I had to:

  1. Disable SIP
  2. Bring up the USB controller interface (in my case there were a couple, so through trial-and-error I found the right one)
  3. Tell Wireshark to capture on that interface
  4. Change the brightness using the Juicy-Link app to generate packets
  5. Save the capture to a file

Step 3: Feed the LLM

With the data from steps 1 and 2 I:

  • Fed them to ChatGPT
  • Mentioned that the capture was from a utility setting the brightness
  • Asked for an equivalent Python script

Here’s what it produced.

Step 4: Run the script in Linux

After installing the pyusb module I could run python set-brightness.py 700 and it worked from Linux! The range from lowest backlight brightness to highest is around 700 – 5000.

The extra bonus (which I’m most excited about) is that sending a value of 0 will truly turn off the backlight! This isn’t even (currently) possible with their software. (Obligatory “do this at your own risk”)

Now I can hook into something like D-Bus signals to potentially toggle the display depending on the desktop’s locked state.

Success

✅ The display’s brightness can be set from any OS, not just macOS.

✅ The display’s backlight can be turned off via software.

Thanks GPT! 🤖

Categories
Apple Coding Fun Nerd Sniped Retro Computing

DOOM on the Apple Watch

I know this has been done, but I hadn’t done it, so it was my weekend nerd snipe. (no game audio)

Edit September 2025: Audio was implemented!

This was a lot easier thanks to doomgeneric!

Basic breakdown

Since doomgeneric exposes the framebuffer, I throw that into an SKTexture and that gets added to a node in the SpriteKit scene, which is subclassed to override the update method to call doomgeneric_Tick(). Objective-C is used for interop between C and Swift, and fulfills most of the functions listed here. SwiftUI ultimately outputs the scene.

Very few tweaks needed to be made in doomgeneric itself.

They were basically:

  • Conditional compilation for a few calls that watchOS didn’t support (and we didn’t need).
  • Tweaking the 32-bit color bit offsets.
  • Handling a crash related to passing in arguments.
    • On watchOS we pass the absolute path of the WAD file in the main bundle to the engine.
  • Adjusting some SDL2 includes so headers could be found.

GitHub repo: https://github.com/twstokes/AppleGenericDoom

Categories
iOS Nerd Sniped Swift

UITextView, autocorrect, and custom attributes

For this week’s “Nerd Snipe”, I spent way too much time at work trying to track down a pesky bug related to our editor. Long story short, we send some custom NSAttributedString.Keys to signify differences in rich text. For instance, a heading attribute with an integer value may tell a parser to wrap text in an <h2> when converting the string to HTML.

Oddly, sometimes that special attribute wouldn’t be included when text was autocorrected, so the generated HTML wasn’t always what we expected. After a lot of digging I believe this could be a bug in UITextView (or maybe NSTextStorage).

Inspecting the value of attrString in the NSTextStorage function func replaceCharacters(in range: NSRange, with attrString: NSAttributedString) after autocorrecting some text shows that it only seems to include attributes that were defined in Foundation (e.g. NSFont, etc.), but not our custom ones.

Regular typing works fine – you see all the expected attributes set in textView.typingAttributes.

In scenarios like these I like to make the simplest example to help confirm I understand it, so I’ve made a repo demonstrating the issue in case it’s helpful for anyone.

Categories
Coding Nerd Sniped Processing

Nerd Sniped

When I saw https://cacheflowe.com/art/digital/deepflat I was “nerd sniped”. I had to figure out how to do it. A bunch of coffees later…

What’s better? I have source code!

https://github.com/twstokes/labs/tree/master/viz/depth_grid