exponent for Fan Control

Here’s another patch for some third party software, this time for keeping your intel mac CPU cool.

I’ve been using Lobotomo’s Fan Control preference pane and daemon to control my 2.16 GHz Core 2 Duo Macbook Pro’s fan speeds since I installed OS X.

It’s useful because it means you can idle with quieter fans in a cool environment, but still have them rev up to max before you burn your fingers. However, one thing that’s annoyed me for a while is that you only really control the floor and ceiling thresholds of a linear response curve.

In my experience (engineers or physicists can correct me), at a constant CPU load, there are multiple equilibrium temperature/fan-speed combinations, but they’re not collinear. In the original Fan Control, the response curve is linear. Taking a hint from gamma correction, I have patched Fan Control 1.2 to use an exponential curve. Where T is the temperature, the target fan speed F is governed by the equation:

F_b + (F_m - F_b) ((T - T_l)/(T_b - T_l))^γ

I also reduced the minimum upper threshold temperature to 60˚C.

You can browse the source in git or download the binary installer. I’ve only tested on the MacBook Pro (running Leopard). Feedback is welcome as comments here on the blog.

Screenshot
exponential curve screenshot

Of course, there may be more effective measures to combat a hot macbook pro.

Decent terminal font

I haven’t been able to find a good replacement for Monaco for use as a terminal font on OS X.

try figuring this one out with bleary eyes
try figuring this one out with bleary eyes

My personal favourite terminal font, neep alt isn’t readily useable from OS X.

None of the Proggy fonts are as clean as Monaco, although Proggy Tiny 11 comes close.

  • lower-case “a” should be double-storey, so it doesn’t look anything like lower-case “o”
  • zero “0” should be slashed
  • lower-case “l” should have a serif on the bottom like lower-case “t”, and not look like numeral “1” or upper-case “I”.
  • upper-case “U” and “V” differ by at least 7 pixels
  • gentle curves rather than blocky rectangles, please

It’s disappointing that Monaco can get so much right and get lower-case “a” wrong. Still, if anyone wants to port neep alt to a format OS X and iTerm can handle (even if it’s only at 13pt semicondensed)… I tried once with fontforge but it was pretty broken.

Twitter not sending 304s

The twitter JSON API that I’ve been using for my status widget has a caching problem, which has caused it to be broken in Opera for a while now. Opera is quite aggressive in re-using its cache (which IMHO is a good thing). However, bad things happen when webservices deviate from the HTTP cache validation model. Twitter is recognising that the browser should be hitting its cache, but its response is broken.

Here’s how it goes:

  1. Load up the JSON document for the first time with an empty cache.
  2. Twitter sends a Last-Modified header and the expected JSON document.
  3. Refresh the document (in Opera, hit enter in the address bar as opposed to clicking the Reload button, since the latter forces a cache refresh). Opera sends an If-Modified-Since header.
  4. Twitter (presumably) recognises that the last status update was not after the browser’s cache timestamp. It sends a degenerate response entity: “[]“; an empty array in javascript, with a 200 OK status.

To test this from a shell:

url='http://twitter.com/statuses/user_timeline/p00ya.json?count=1&callback=f'
lm="$(wget --debug $url 2>&1 \
 |grep '^Last-Modified:' \
 |sed -e 's/Last-Modified/If-Modified-Since/' \
 |tr -d '\n\r')"
[ x != "x$lm" ] && \
  wget -nv --save-headers -O - --header="$lm" $url

The brokeness comes from the half-baked response. A 200 OK status code would be fine if the full JSON object was written out. A 304 status code with any kind of entity would be fine too. The empty array might even prevent breakage in user-agents that don’t handle 304s (but do send If-Modified-Since? wtf?). Sending a 200 response overwrites the correct cached entity, replacing it with the degenerate response.

Firefox seems to be unaffected since it doesn’t cache the document at all and so doesn’t send the If-Modified-Since header.

One workaround is to use something like jQuery’s cache breaking capability (where it adds some random tokens to the URL each time). I refuse. Just remember the widget breakage wasn’t my fault!

A separate issue is that it seemed to break Opera quite badly. Perhaps it was because I’m using jQuery’s ready event, but Opera hangs as if the XHR was synchronous. The document wouldn’t receive any events (no mouse-wheel scrolling). I’ve got no idea why though; my callback functions are robust enough to handle being passed the empty array twitter calls them with, and I wasn’t getting any exceptions.