<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;AkUFQng5eyp7ImA9WxBUFEQ.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357</id><updated>2010-03-02T15:50:13.623+13:00</updated><title>OneSadCookie</title><subtitle type="html">Random Ramblings about Macintosh Programming</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.onesadcookie.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>31</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/OneSadCookie" /><feedburner:info uri="onesadcookie" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;C0EARXg4cCp7ImA9WxZXEEo.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-2381389904646414860</id><published>2008-02-27T13:18:00.001+13:00</published><updated>2008-02-27T13:20:44.638+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-02-27T13:20:44.638+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="apology" /><category scheme="http://www.blogger.com/atom/ns#" term="imageio" /><category scheme="http://www.blogger.com/atom/ns#" term="coregraphics" /><title>ImageIO is thread-safe after all</title><content type="html">&lt;p&gt;In &lt;a href="http://blog.onesadcookie.com/2008/02/imageio-is-not-thread-safe.html"&gt;ImageIO is not thread-safe&lt;/a&gt; I reported a problem with ImageIO and threading.  I now believe my analysis was wrong, and ImageIO is thread-safe after all.  Serializing my ImageIO loading had the effect of hiding the race condition in my own code, so the error became very rare.  My apologies to anyone misled by the post, and to anyone at Apple who had to deal with my bug report.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-2381389904646414860?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/Npw7yD54BzU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/2381389904646414860/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=2381389904646414860" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/2381389904646414860?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/2381389904646414860?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/Npw7yD54BzU/imageio-is-thread-safe-after-all.html" title="ImageIO is thread-safe after all" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2008/02/imageio-is-thread-safe-after-all.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0AHSXs_eSp7ImA9WxZXEEo.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-257610246945155950</id><published>2008-02-22T16:34:00.004+13:00</published><updated>2008-02-27T13:22:18.541+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-02-27T13:22:18.541+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="bug" /><category scheme="http://www.blogger.com/atom/ns#" term="imageio" /><category scheme="http://www.blogger.com/atom/ns#" term="quicktime" /><category scheme="http://www.blogger.com/atom/ns#" term="threading" /><category scheme="http://www.blogger.com/atom/ns#" term="coregraphics" /><title>ImageIO is not thread-safe</title><content type="html">&lt;p&gt;&lt;b&gt;This post is wrong.  See &lt;a href="http://blog.onesadcookie.com/2008/02/imageio-is-thread-safe-after-all.html"&gt;ImageIO is thread-safe after all&lt;/a&gt; for correction.&lt;/b&gt;&lt;/p&gt;&lt;p&gt;I can't find any documentation one way or another, and assumed that as a modern API it was OK to call ImageIO from multiple background threads.  It appears that this is not the case.  &lt;code&gt;CGCreateImageSourceFromURL&lt;/code&gt; fails randomly if you do; always calling it from the main thread appears to work.&lt;/p&gt;&lt;p&gt;Radar 5759067 requests that this preferably be fixed, or if not, at least documented.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-257610246945155950?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/jPQcEwOofVY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/257610246945155950/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=257610246945155950" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/257610246945155950?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/257610246945155950?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/jPQcEwOofVY/imageio-is-not-thread-safe.html" title="ImageIO is not thread-safe" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2008/02/imageio-is-not-thread-safe.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkEFRXo_cCp7ImA9WxZQE0s.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-847900954855191444</id><published>2008-02-19T09:38:00.003+13:00</published><updated>2008-02-19T10:03:34.448+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-02-19T10:03:34.448+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="limerick" /><category scheme="http://www.blogger.com/atom/ns#" term="random" /><category scheme="http://www.blogger.com/atom/ns#" term="delicious monster" /><title>A Delicious Limerick</title><content type="html">&lt;pre&gt;&lt;code&gt;How &lt;strong&gt;does&lt;/strong&gt; he create all that &lt;a href="http://www.viddler.com/explore/rentzsch/videos/4/"&gt;buzz&lt;/a&gt;?
&lt;a href="http://www.wilshipley.com/"&gt;Wil Shipley&lt;/a&gt; says "Easy, it's 'cuz
    "for &lt;a href="http://www.delicious-monster.com/"&gt;Delicious Lib'ry&lt;/a&gt;,
    "I've the weight of &lt;a href="http://atomicwang.org/motherfucker/"&gt;Mike Lee&lt;/a&gt;!
"Who says size doesn't matter?  It does!"&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With apologies to Mike (and Wil), and thanks to &lt;a href="http://limerickdb.com/?top150"&gt;limerickdb&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-847900954855191444?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/1957dY_WQZY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/847900954855191444/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=847900954855191444" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/847900954855191444?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/847900954855191444?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/1957dY_WQZY/delicious-limerick.html" title="A Delicious Limerick" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2008/02/delicious-limerick.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMBSXg4eCp7ImA9WxZQEkU.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-1440297826952883789</id><published>2008-02-18T10:21:00.002+13:00</published><updated>2008-02-18T10:24:18.630+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-02-18T10:24:18.630+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="curl" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Multithreaded libcurl Crash</title><content type="html">&lt;pre&gt;&lt;code&gt;Thread 0 Crashed:
0   libcurl.4.dylib                0x00391e1c curl_unescape + 78
1   libcurl.4.dylib                0x003929c0 curl_unescape + 3058
2   libcurl.4.dylib                0x00393978 curl_mvsnprintf + 55
3   libcurl.4.dylib                0x003850f9 Curl_failf + 63
4   libcurl.4.dylib                0x0037d8da Curl_resolv + 95&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://curl.haxx.se/mail/lib-2005-11/0127.html"&gt;Apparently&lt;/a&gt; CURLOPT_NOSIGNAL is &lt;span style="font-weight:bold;"&gt;very&lt;/span&gt; important in multithreaded programs...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-1440297826952883789?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/Xb1aU8jspjc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/1440297826952883789/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=1440297826952883789" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/1440297826952883789?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/1440297826952883789?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/Xb1aU8jspjc/multithreaded-libcurl-crash.html" title="Multithreaded libcurl Crash" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2008/02/multithreaded-libcurl-crash.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8FQHs8fSp7ImA9WxZRGEk.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-2308180060031777714</id><published>2008-02-13T09:14:00.000+13:00</published><updated>2008-02-13T09:23:31.575+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-02-13T09:23:31.575+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rant" /><category scheme="http://www.blogger.com/atom/ns#" term="codesign" /><category scheme="http://www.blogger.com/atom/ns#" term="leopard" /><title>Leopard doesn't verify code signatures?</title><content type="html">&lt;p&gt;With all the fuss about code signatures in Leopard, I expected that the system would verify signed applications when they're launched, and prompt the user if the signature is incorrect.  It doesn't.&lt;/p&gt;&lt;p&gt;To test this, first verify that TextEdit's signature is valid:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;codesign -v /Applications/TextEdit.app/&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(It should print nothing).  Then edit &lt;code&gt;/Applications/TextEdit.app/Contents/Info.plist&lt;/code&gt;.  Make sure the edit is simple enough that you can undo it &amp;mdash; changing the version number is an easy option.  Now verify the signature again:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;codesign -v /Applications/TextEdit.app/
/Applications/TextEdit.app/: code or signature modified&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now open TextEdit.  It opens without warning.  We could have replaced the executable with malicious code, and we wouldn't have been warned.&lt;/p&gt;&lt;p&gt;Make sure you revert your change to Info.plist and re-verify the application.&lt;/p&gt;&lt;p&gt;Since Tiger doesn't have any safeguards of this kind, obviously the situation is no worse than Tiger, but I don't understand what the point of code signing is if the OS doesn't make use of it to make the user's experience that little bit safer.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-2308180060031777714?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/cIhprNfElIY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/2308180060031777714/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=2308180060031777714" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/2308180060031777714?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/2308180060031777714?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/cIhprNfElIY/leopard-doesnt-verify-code-signatures.html" title="Leopard doesn't verify code signatures?" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2008/02/leopard-doesnt-verify-code-signatures.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0AGSX4zcCp7ImA9WxZTEUo.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-2487899773606130635</id><published>2008-01-13T09:14:00.000+13:00</published><updated>2008-01-13T10:02:08.088+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-01-13T10:02:08.088+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="opengl" /><title>CFPreferences keys for OpenGL</title><content type="html">&lt;p&gt;Mac OS X's OpenGL framework (only Leopard tested) reads a bunch of values from defaults at app startup.  They are:&lt;/p&gt;&lt;h4&gt;Integer values&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;ColorBufferSizeKey
DepthBufferSizeKey
MultisampleKey
RendererIDKey
ThreadedEngineKey
AcceleratedMethodKey
MaxSwapsInFlightKey&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;Boolean values&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;ColorBufferSizeEnableKey
DepthBufferSizeEnableKey
VBLSyncEnableKey
MultisampleEnableKey
RendererIDEnableKey
AllowOfflineKey&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I haven't been able to make these do anything... anyone know how to use them?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-2487899773606130635?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/BVkq_LOiZpc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/2487899773606130635/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=2487899773606130635" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/2487899773606130635?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/2487899773606130635?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/BVkq_LOiZpc/cfpreferences-keys-for-opengl.html" title="CFPreferences keys for OpenGL" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2008/01/cfpreferences-keys-for-opengl.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4CQ3YyeSp7ImA9WB9aE08.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-3096313479614404962</id><published>2008-01-03T14:37:00.000+13:00</published><updated>2008-01-03T15:22:42.891+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-01-03T15:22:42.891+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="dynamic linking" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="install_name" /><title>install_name Magic</title><content type="html">&lt;p&gt;Mac OS X application packages allow you to embed frameworks and dynamic libraries within your application, making it easy to comply with the LGPL or use a third-party library without infringing on your users' convenience.  However, actually making this work isn't as straightforward as perhaps it should be...&lt;/p&gt;&lt;p&gt;Dynamic libraries (and frameworks, same thing) on Mac OS X include an "install_name".  This is the absolute path that the library expects to reside at.  When a program is linked, the install_names from any dynamic libraries it links against are copied into the program, and dyld looks for the libraries at these paths when the program is run.&lt;/p&gt;&lt;p&gt;That means that in order to embed a dynamic library into your application bundle, your program must contain an install_name for the library which points to a path inside the application bundle, and the easiest way to get the correct path into your program is to have the correct install_name in the library itself.&lt;/p&gt;&lt;p&gt;Wait, didn't I just say that the install_name is an absolute path?  How can it point into the application bundle, when you don't know where the application bundle is?  Fortunately there's a special token &lt;code&gt;@executable_path&lt;/code&gt; that can appear at the beginning of an install_name that refers to the absolute path of the program loading the library.&lt;/p&gt;&lt;p&gt;Where does all this leave us?&lt;/p&gt;&lt;p&gt;If possible, you'll build your dynamic library with the appropriate install_name&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ gcc foo.c -dynamiclib -o libfoo.dylib -install_name @executable_path/../Libraries/libfoo.dylib
$ otool -L libfoo.dylib 
libfoo.dylib:
 @executable_path/../Libraries/libfoo.dylib (compatibility version 0.0.0, current version 0.0.0)
 /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
 /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.0.0)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then you'll link your application as normal, and when you put the library in Contents/Libraries everything will just work&amp;trade;&lt;/p&gt;&lt;p&gt;If building the dynamic library appropriately isn't an option (a closed-source third-party library for example), don't worry.  It's the install_name copied to your program that's really important, and that can be fudged.  You'll link your application with the &lt;code&gt;-headerpad_max_install_names&lt;/code&gt; flag, then edit it with &lt;code&gt;install_name_tool&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ gcc main.c -L. -lfoo -headerpad_max_install_names
$ otool -L a.out
a.out:
 /usr/local/lib/libfoo.dylib (compatibility version 0.0.0, current version 0.0.0)
 /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
 /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.0.0)
$ install_name_tool -change /usr/local/lib/libfoo.dylib @executable_path/../Libraries/libfoo.dylib a.out
$ otool -L a.out
a.out:
 @executable_path/../Libraries/libfoo.dylib (compatibility version 0.0.0, current version 0.0.0)
 /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
 /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.0.0)&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-3096313479614404962?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/KjgboB7BRiU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/3096313479614404962/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=3096313479614404962" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/3096313479614404962?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/3096313479614404962?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/KjgboB7BRiU/installname-magic.html" title="install_name Magic" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2008/01/installname-magic.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C08CRnwzfip7ImA9WB9bE0o.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-1200340791050524852</id><published>2007-12-23T13:49:00.000+13:00</published><updated>2007-12-23T14:04:27.286+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-23T14:04:27.286+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="bug" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Ruby.framework broken in MacOSX10.5.sdk</title><content type="html">&lt;p&gt;If you're trying to build against Ruby.framework on Mac OS X 10.5 you may have come across a problem with the SDK.  This manifests itself as this error:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;gcc test.c -framework Ruby -mmacosx-version-min=10.5 -isysroot /Developer/SDKs/MacOSX10.5.sdk
ld: framework not found Ruby
collect2: ld returned 1 exit status&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This appears to be fixable like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;cd /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/Ruby.framework/Versions/Current
sudo ln -s usr/lib/libruby.dylib Ruby&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I don't know whether that's the right fix, but it seems to do the trick.  Radar 5661551.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-1200340791050524852?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/2CBtV48a8Zc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/1200340791050524852/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=1200340791050524852" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/1200340791050524852?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/1200340791050524852?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/2CBtV48a8Zc/rubyframework-broken-in-macosx105sdk.html" title="Ruby.framework broken in MacOSX10.5.sdk" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/12/rubyframework-broken-in-macosx105sdk.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUUFRH45fip7ImA9WxZTEkQ.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-9164070072607371209</id><published>2007-12-13T09:09:00.000+13:00</published><updated>2008-01-14T20:53:35.026+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-01-14T20:53:35.026+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gcc" /><category scheme="http://www.blogger.com/atom/ns#" term="faq" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>GCC Warning Flags</title><content type="html">&lt;p&gt;Xcode's default warning settings are useless, not informing you of many real problems in your source code.  I compile all my code with this set of warning flags:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-Wall -Wextra -Wno-unused-parameter -Wnewline-eof -Werror&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;&lt;code&gt;-Wall&lt;/code&gt;&lt;/h4&gt;&lt;p&gt;These are warnings that always indicate a problem with your code.  I have no idea why they''re not enabled by default.&lt;/p&gt;&lt;h4&gt;&lt;code&gt;-Wextra&lt;/code&gt;&lt;/h4&gt;&lt;p&gt;Also known as &lt;code&gt;-W&lt;/code&gt;.  These are warnings that indicate potential problems with your code.  They will often appear in code that's correct, but they will also often expose code that's incorrect.&lt;/p&gt;&lt;h4&gt;&lt;code&gt;-Wno-unused-parameter&lt;/code&gt;&lt;/h4&gt;&lt;p&gt;&lt;code&gt;-Wextra&lt;/code&gt; warns about unused parameters to functions.  Since this is a common occurrence in object-oriented code, I prefer not to see these warnings.&lt;/p&gt;&lt;h4&gt;&lt;code&gt;-Wnewline-eof&lt;/code&gt;&lt;/h4&gt;&lt;p&gt;Warns if a source file doesn''t end with a newline character.  If you're just coding for the Mac, you can ignore this one, but GCC on Linux will error in this  situation, so if you're writing portable code, it's nice to know about it.&lt;/p&gt;&lt;h4&gt;&lt;code&gt;-Werror&lt;/code&gt;&lt;/h4&gt;&lt;p&gt;Treats all warnings as errors.  To make sure your code is top quality!  It's easy to lose warnings in files that aren't changed often.  It's impossible to lose errors.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-9164070072607371209?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/qsjm4SHnGyg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/9164070072607371209/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=9164070072607371209" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/9164070072607371209?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/9164070072607371209?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/qsjm4SHnGyg/gcc-warning-flags.html" title="GCC Warning Flags" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/12/gcc-warning-flags.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk4BRnk6fSp7ImA9WxRVFUo.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-8747941924529721914</id><published>2007-12-12T15:15:00.000+13:00</published><updated>2008-11-14T00:15:57.715+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-14T00:15:57.715+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="faq" /><category scheme="http://www.blogger.com/atom/ns#" term="xcode" /><category scheme="http://www.blogger.com/atom/ns#" term="tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="opengl" /><category scheme="http://www.blogger.com/atom/ns#" term="glut" /><title>Xcode/GLUT Tutorial</title><content type="html">&lt;p&gt;&lt;span style="font-style:italic;"&gt;This was the most popular thing on my old site.  I've updated it for Xcode 3 on Leopard, but that means that the instructions may be a little misleading for Xcode 2 or earlier...&lt;/span&gt;&lt;h4&gt;Xcode?  OpenGL?  GLUT?&lt;/h4&gt;&lt;p&gt;Xcode is Apple's integrated software development environment.&lt;/p&gt;&lt;p&gt;OpenGL is a graphics library.  It does both 2D and 3D graphics, and takes advantage of hardware acceleration.  It's cross-platform; that is, the same code will run on the Mac, Windows and Linux.&lt;/p&gt;&lt;p&gt;OpenGL itself is cross-platform, but each OS has a different way of making an OpenGL window and getting events.  GLUT is a simple cross-platform wrapper for this functionality.&lt;/p&gt;&lt;h4&gt;Making a new project&lt;/h4&gt;&lt;p&gt;Open the Xcode application.  You'll find it in &lt;code&gt;/Developer/Applications/&lt;/code&gt;:&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_cVAaWltDN2c/R19FfkvoKNI/AAAAAAAAADc/MQ5jybIVgXg/s1600-h/xcode-finder.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_cVAaWltDN2c/R19FfkvoKNI/AAAAAAAAADc/MQ5jybIVgXg/s400/xcode-finder.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5142905708222097618" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;From Xcode's File menu, choose "New Project...".  Select "Empty Project" from the list, then click "Next":&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_cVAaWltDN2c/R19F40voKOI/AAAAAAAAADk/DZyU1NEduLA/s1600-h/xcode-newproject.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_cVAaWltDN2c/R19F40voKOI/AAAAAAAAADk/DZyU1NEduLA/s400/xcode-newproject.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5142906142013794530" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Xcode will ask you what to call your project and where to save it.  I'm going to call mine "XcodeGLUT" and put it on my Desktop.&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_cVAaWltDN2c/R19GMkvoKPI/AAAAAAAAADs/rVyhekdz2F4/s1600-h/xcode-saveproject.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_cVAaWltDN2c/R19GMkvoKPI/AAAAAAAAADs/rVyhekdz2F4/s400/xcode-saveproject.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5142906481316210930" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Now click "Finish".  Your project's window will appear:&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_cVAaWltDN2c/R19GcUvoKQI/AAAAAAAAAD0/Ble6UwVqIkk/s1600-h/xcode-projectwindow.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_cVAaWltDN2c/R19GcUvoKQI/AAAAAAAAAD0/Ble6UwVqIkk/s400/xcode-projectwindow.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5142906751899150594" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h4&gt;Making a GLUT target&lt;/h4&gt;&lt;p&gt;Xcode projects can have any number of &lt;span style="font-style:italic;"&gt;target&lt;/span&gt;s.  A target describes how to make a &lt;span style="font-style:italic;"&gt;product&lt;/span&gt; such as an application, library, framework or plug-in.&lt;/p&gt;&lt;p&gt;Our empty project didn't come with a target, so we need to make one.  From the "Project" menu, choose "New Target...". Choose "Cocoa Application" and click "Next":&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_cVAaWltDN2c/R19HCUvoKRI/AAAAAAAAAD8/mG-uTdH9gk4/s1600-h/xcode-newtarget.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_cVAaWltDN2c/R19HCUvoKRI/AAAAAAAAAD8/mG-uTdH9gk4/s400/xcode-newtarget.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5142907404734179602" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;You can name your target whatever you like.  I'm going to call mine "XcodeGLUT".  Then click "Finish":&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_cVAaWltDN2c/R19HXkvoKSI/AAAAAAAAAEE/KesxRPYZ0OI/s1600-h/xcode-targetname.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_cVAaWltDN2c/R19HXkvoKSI/AAAAAAAAAEE/KesxRPYZ0OI/s400/xcode-targetname.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5142907769806399778" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Xcode presents you with your new target's settings.  Xcode's default Cocoa Application template is designed for writing a Cocoa application in Objective C.  We're just writing a GLUT application in C, so we need to make a small adjustment to the target settings.  Change the value of the GCC_PREFIX_HEADER setting to be empty:&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_cVAaWltDN2c/R19IB0voKTI/AAAAAAAAAEM/-UkHWaJcv40/s1600-h/xcode-prefixheader.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_cVAaWltDN2c/R19IB0voKTI/AAAAAAAAAEM/-UkHWaJcv40/s400/xcode-prefixheader.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5142908495655872818" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;In order to compile a GLUT application we need a couple of extra frameworks.  Right- or control-click on your target's icon, and choose "Add &amp;#x25b8; Existing Frameworks...".  You'll be presented with an Open dialog showing &lt;code&gt;/System/Library/Frameworks&lt;/code&gt;.  If you have trouble with the contextual menu, you can also use "Add to Project..." in the "Project" menu, but you'll need to navigate to &lt;code&gt;/System/Library/Frameworks&lt;/code&gt; yourself.  Choose &lt;span style="font-weight:bold;"&gt;both&lt;/span&gt; GLUT.framework and OpenGL.framework.  You can use command (&amp;#x2318;)-click to select both at once.  Then click "Add":&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_cVAaWltDN2c/R19Jq0voKUI/AAAAAAAAAEU/JxXIKT3MNsc/s1600-h/xcode-addframeworks.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_cVAaWltDN2c/R19Jq0voKUI/AAAAAAAAAEU/JxXIKT3MNsc/s400/xcode-addframeworks.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5142910299542137154" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Xcode will ask you for some settings for the added frameworks; the defaults are fine.  Just click "Add":&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_cVAaWltDN2c/R19KIEvoKVI/AAAAAAAAAEc/FNI-QBNdsKw/s1600-h/xcode-frameworkoptions.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_cVAaWltDN2c/R19KIEvoKVI/AAAAAAAAAEc/FNI-QBNdsKw/s400/xcode-frameworkoptions.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5142910802053310802" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Finally, we need to make a file for the source code.  From the "File" menu, choose "New File...".  Select "Empty File in Project", then click "Next":&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_cVAaWltDN2c/R19KY0voKWI/AAAAAAAAAEk/cqp2smb97Og/s1600-h/xcode-newfile.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_cVAaWltDN2c/R19KY0voKWI/AAAAAAAAAEk/cqp2smb97Og/s400/xcode-newfile.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5142911089816119650" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Xcode will ask you what you want to call the file, and where you want to save it.  Call it "main.c"; the default settings for everything else are fine.  click "Finish":&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_cVAaWltDN2c/R19KnEvoKXI/AAAAAAAAAEs/vg30nJrHp2E/s1600-h/xcode-savefile.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_cVAaWltDN2c/R19KnEvoKXI/AAAAAAAAAEs/vg30nJrHp2E/s400/xcode-savefile.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5142911334629255538" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h4&gt;Building your first Application&lt;/h4&gt;&lt;p&gt;Copy and paste the following code into "main.c".  Don't worry at this stage what it all means, we'll go through it line-by-line later:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdlib.h&amp;gt;

#include &amp;lt;GLUT/glut.h&amp;gt;

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glutSwapBuffers();
}

void reshape(int width, int height)
{
    glViewport(0, 0, width, height);
}

void idle(void)
{
    glutPostRedisplay();
}

int main(int argc, char** argv)
{
    glutInit(&amp;argc, argv);
    
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(640, 480);
    
    glutCreateWindow("GLUT Program");
    
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutIdleFunc(idle);
    
    glutMainLoop();
    return EXIT_SUCCESS;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Save the file, then click "Build and Go" on the toolbar, or choose "Build and Run" from the "Build" menu.  After some time, your new application should run.  At this stage, it's just a black window.  When you're done admiring your handiwork, quit it and return to Xcode.&lt;/p&gt;&lt;h4&gt;What does all that code mean?&lt;/h4&gt;&lt;p&gt;This section assumes you're not totally new to programming in C.  If you are, you should probably go away now and find a quick introduction to the language.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdlib.h&amp;gt;

#include &amp;lt;GLUT/glut.h&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;stdlib.h includes some fundamental constants and functions for programming in C.  GLUT/glut.h includes the constants and functions for GLUT and OpenGL.  On Linux and Windows, GLUT/glut.h is called GL/glut.h.&lt;/p&gt;&lt;pre&gt;&lt;/code&gt;int main(int argc, char** argv)
{
    glutInit(&amp;argc, argv);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;main is where the program begins executing.  argc and argv are used to pass command-line arguments to the program; you needn't worry about them other than that they get passed to glutInit. glutInit is always the first function you call in a GLUT program; Bad Things&amp;#x2122; will happen if it's not.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(640, 480);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The call to glutInitDisplayMode here is telling GLUT that all our windows should be in red-green-blue-alpha mode, be double-buffered (to avoid flickering when drawing), and have a depth buffer (to ensure that 3D objects overlap correctly regardless of the order they're drawn in).  The call to glutInitWindowSize tells GLUT that our windows should initially be 640 pixels wide by 480 pixels high.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;    glutCreateWindow("GLUT Program");&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The call to glutCreateWindow actually makes a window.  The string passed is the title of the window.  Until we've created a window, we can't make any OpenGL calls.  Now, it's safe.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutIdleFunc(idle);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;glutDisplayFunc, glutReshapeFunc and glutIdleFunc tell GLUT to call particular functions when certain things happen.  The function we pass to glutDisplayFunc is called whenever it's time to draw a frame; the function we pass to glutReshapeFunc is called whenever the window is resized; and the function we pass to glutIdleFunc is called whenever GLUT isn't doing anything else.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;    glutMainLoop();
    return EXIT_SUCCESS;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, glutMainLoop tells GLUT to go off and do its thing, calling the functions we specified before whenever the appropriate things happen.  glutMainLoop never returns, so returning EXIT_SUCCESS at the bottom is redundant, but it pacifies the compiler (which doesn't know that glutMainLoop never returns).&lt;/p&gt;&lt;pre&gt;&lt;code&gt;void idle(void)
{
    glutPostRedisplay();
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Our idle function simply tells GLUT that we'd like to draw another frame now.  This, combined with the fact that idle will be called whenever GLUT is not doing anything else, ensures that our program will run at the maximum possible frame rate.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;void reshape(int width, int height)
{
    glViewport(0, 0, width, height);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Our reshape function simply informs OpenGL that the window size has changed.  width and height are the new width and height of the window in pixels.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glutSwapBuffers();
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Our display function asks OpenGL to clear the color and depth buffers, then to swap the back buffer onto the screen.&lt;/p&gt;&lt;p&gt;Because we asked for a double-buffered window, we have two mages, called the front and back buffer.  The front buffer is what's visible on-screen in the window.  All OpenGL commands modify the back buffer.  The glutSwapBuffers call swaps the back buffer to become the front buffer, instantly making all our drawing visible.&lt;/p&gt;&lt;p&gt;The color buffer is cleared to black by default; this can be changed with the glClearColor function.&lt;/p&gt;&lt;h4&gt;Actually drawing something&lt;/h4&gt;&lt;p&gt;First, let's draw a white rectangle.  Edit the display function so it looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glBegin(GL_QUADS);
    glVertex2f(-0.75f, -0.75f);
    glVertex2f( 0.75f, -0.75f);
    glVertex2f( 0.75f,  0.75f);
    glVertex2f(-0.75f,  0.75f);
    glEnd();
    
    glutSwapBuffers();
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again, save the file and "Build and Go".  You should see a white rectangle filling most of the window.&lt;/p&gt;&lt;p&gt;A "Quad" is OpenGL's term for a four-sided polygon.  The four glVertex2f calls say where the vertices (corners) of this polygon ie.  By default, OpenGL's coordinate system has (-1, -1) in the lower left corner of the window, and (1, 1) in the upper right. Note that since our window isn't square, we get a rectangle rather than a square, as you might expect.  We'll see how to fix this in the next section.&lt;/p&gt;&lt;h4&gt;A pixel-perfect projection&lt;/h4&gt;&lt;p&gt;In OpenGL, you can use whatever coordinate system is useful to you.  One of the most useful for 2D graphics is the projection where OpenGL units are precisely one pixel.&lt;/p&gt;&lt;p&gt;Edit the reshape function to look like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;void reshape(int width, int height)
{
    glViewport(0, 0, width, height);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, width, 0, height);
    glMatrixMode(GL_MODELVIEW);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you've done any linear algebra, that will make some kind of sense to you.  If not, don't worry too much &amp;mdash; just accept it as some necessary mumbo-jumbo.&lt;/p&gt;&lt;p&gt;glMatrixMode tells OpenGL which of the matrices it uses for projecting OpenGL coordinates onto pixels we want to modify. Most changes are done to the model-view matrix, but in this case we want to change the projection.  glLoadIdentity resets the matrix to the identity matrix, and gluOrtho2D sets up the 2D projection.  The arguments to gluOrtho2D are left, right, bottom, top.  Note that in OpenGL it's usual to put (0, 0) in the lower left (as is usual in mathematics) rather than the upper left (as is usual in computer graphics).&lt;/p&gt;&lt;p&gt;With this change to the coordinate system, the quad we're drawing is only three quarters of a pixel in size, right in the bottom left of the window.  We should probably change that:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;    glBegin(GL_QUADS);
    glVertex2f(  0.0f,   0.0f);
    glVertex2f(128.0f,   0.0f);
    glVertex2f(128.0f, 128.0f);
    glVertex2f(  0.0f, 128.0f);
    glEnd();&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again, build and run.  You should see a 128- by 128-pixel white square in the lower left of the window.&lt;/p&gt;&lt;h4&gt;Animation and timing&lt;/h4&gt;&lt;p&gt;Let's get this square moving.  How are we going to do that? One obvious way of doing it would be to move it a certain number of pixels every frame.  That has a serious flaw, however &amp;mdash; the speed of the square would be relative to the speed of the computer, since a faster computer will draw more frames per second than a slower one.  That means we have to build the actual amount of time elapsed into our movement calculations.&lt;/p&gt;&lt;p&gt;GLUT has a handy way to get the number of milliseconds that have passed since glutInit() was called &amp;mdash; glutGet(GLUT_ELAPSED_TIME).  We'll want to work with the time in seconds, so we'll need to divide the number we get back by 1000.&lt;/p&gt;&lt;p&gt;Let's get this square moving.  We'll move it right at 512 pixels per second, and when it's past the edge of the window, we'll move it back to the left.&lt;/p&gt;&lt;p&gt;We can also use glutGet to find the current width of the window in pixels, rather than storing it ourselves in the reshape function. We'll use this to make sure the square always goes to the edge of the window.&lt;/p&gt;&lt;p&gt;We'll need two global variables.  One (lastFrameTime) to store the number of milliseconds elapsed at the last frame, so we know how much time we need to account for this frame, and one (boxX) to store the horizontal position of the box.  These go just under the #includes:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;int lastFrameTime = 0;
    
float boxX = 0.0f;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, we need to rewrite the display function:&lt;/p&gt;&lt;p&gt;First, we check if lastFrameTime is still zero.  If it is, this is the first frame, and we need to set it to something sensible.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;void display(void)
{
    if (lastFrameTime == 0)
    {
        lastFrameTime = glutGet(GLUT_ELAPSED_TIME);
    }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we calculate how many seconds have elapsed since the previous frame.  We get the current time, calculate how many milliseconds have elapsed since the last frame, convert that to seconds (being very careful to divide by a floating point number rather than an integer, which would give zero), then set lastFrameTime to the current time, in preparation for the next frame.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;    int now = glutGet(GLUT_ELAPSED_TIME);
    int&lt;/span&gt; elapsedMilliseconds = now - lastFrameTime;
    float&lt;/span&gt; elapsedTime = elapsedMilliseconds / 1000.0f;
    lastFrameTime = now;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we retrieve the window width in pixels from GLUT, then move our box.  It goes right by 512 pixels every second, so we multiply 512 by the number of seconds elapsed, and add that to its current position.  If it's now beyond the edge of the window, we move it back to the start again.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;    int windowWidth = glutGet(GLUT_WINDOW_WIDTH);
    
    boxX += 512.0f * elapsedTime;
    if (boxX &amp;gt; windowWidth)
    {
        boxX -= windowWidth;
    }&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We clear the color and depth bufers as usual.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;glTranslatef(x, y, z) moves everything drawn after it the specified number of units.  We'll use this command to draw the square in a different place, rather than calculating the new vertex positions by hand.  Because we don't want this change to apply over multiple frames, we use glPushMatrix and glPopMatrix around the commands we want to be affected.  We draw the square and swap the buffers just as before.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;    glPushMatrix();
    glTranslatef(boxX, 0.0f, 0.0f);
    
    glBegin(GL_QUADS);
    glVertex2f(  0.0f,   0.0f);
    glVertex2f(128.0f,   0.0f);
    glVertex2f(128.0f, 128.0f);
    glVertex2f(  0.0f, 128.0f);
    glEnd();
    glPopMatrix();
    
    glutSwapBuffers();
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Build and run the program again.  Notice that no matter how jerky the movement is, the square always moves at the same speed.  Try resizing the window, or running another OpenGL game at the same time to make it jerky.&lt;/p&gt;&lt;p&gt;Also notice that no matter how big the window is, the square always moves at the same speed, and always goes back when it eaches the window's edge.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-8747941924529721914?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/6kX4_g-aliw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/8747941924529721914/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=8747941924529721914" title="31 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/8747941924529721914?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/8747941924529721914?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/6kX4_g-aliw/xcodeglut-tutorial.html" title="Xcode/GLUT Tutorial" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_cVAaWltDN2c/R19FfkvoKNI/AAAAAAAAADc/MQ5jybIVgXg/s72-c/xcode-finder.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">31</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/12/xcodeglut-tutorial.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0QFQno6fip7ImA9WB9VGUw.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-970362657083363174</id><published>2007-12-06T16:18:00.000+13:00</published><updated>2007-12-06T16:21:53.416+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-06T16:21:53.416+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="files" /><category scheme="http://www.blogger.com/atom/ns#" term="cwd" /><category scheme="http://www.blogger.com/atom/ns#" term="faq" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Finding Your App's Files</title><content type="html">&lt;p&gt;The current working directory for your application depends on how it was run, and what API you're using:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;If your application is using GLUT, the current working directory is your bundle's &lt;code&gt;Resources&lt;/code&gt; directory.&lt;/li&gt;&lt;li&gt;If your application is using SDL, the current working directory is the directory containing your application bundle, by default.  This is set in SDLMain.m.  You may wish to change it (see below).&lt;/li&gt;&lt;li&gt;If you run your application from Xcode, the current working directory is the directory containing your application bundle.&lt;/li&gt;&lt;li&gt;If you run your application from Finder, the current working directory is &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;&lt;li&gt;If you run your application from the command-line, the current working directory is the current working directory of the controlling shell.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Generally, you'll want the current working directory to be your bundle's &lt;code&gt;Resources&lt;/code&gt; directory.  You'll set it correctly once at startup.&lt;/p&gt;&lt;h4&gt;Cocoa&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
[[NSFileManager defaultManager] changeCurrentDirectoryPath:resourcePath];&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;CoreFoundation&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
char path[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
{
    // error!
}
CFRelease(resourcesURL);
chdir(path);&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-970362657083363174?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/6fDxFXKI6As" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/970362657083363174/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=970362657083363174" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/970362657083363174?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/970362657083363174?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/6fDxFXKI6As/finding-your-apps-files.html" title="Finding Your App's Files" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/12/finding-your-apps-files.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak8BR38ycCp7ImA9WB9VF04.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-6485224195831592672</id><published>2007-12-04T16:26:00.000+13:00</published><updated>2007-12-04T16:27:36.198+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-04T16:27:36.198+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="psa" /><category scheme="http://www.blogger.com/atom/ns#" term="idevgames" /><title>iDevGames is Back</title><content type="html">&lt;p&gt;Carlos blames "IP address issues".  I'm skeptical.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-6485224195831592672?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/sI1mGUHiE18" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/6485224195831592672/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=6485224195831592672" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/6485224195831592672?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/6485224195831592672?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/sI1mGUHiE18/idevgames-is-back.html" title="iDevGames is Back" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/12/idevgames-is-back.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEENRHwyeSp7ImA9WB9VFk4.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-7376107529562001208</id><published>2007-12-03T12:03:00.000+13:00</published><updated>2007-12-03T12:04:55.291+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-03T12:04:55.291+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="createmacgames" /><category scheme="http://www.blogger.com/atom/ns#" term="psa" /><title>CreateMacGames Lives!</title><content type="html">&lt;p&gt;Well... kinda.  Boards at &lt;a href="http://cmg.onesadcookie.net"&gt;http://cmg.onesadcookie.net&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-7376107529562001208?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/M99hzHO05zA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/7376107529562001208/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=7376107529562001208" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/7376107529562001208?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/7376107529562001208?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/M99hzHO05zA/createmacgames-lives.html" title="CreateMacGames Lives!" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/12/createmacgames-lives.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkIBR389eCp7ImA9WB9VFk4.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-8823402913962453913</id><published>2007-12-03T10:15:00.001+13:00</published><updated>2007-12-03T10:22:36.160+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-03T10:22:36.160+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="psa" /><category scheme="http://www.blogger.com/atom/ns#" term="idevgames" /><category scheme="http://www.blogger.com/atom/ns#" term="downtime" /><title>iDevGames is Down</title><content type="html">&lt;p&gt;iDevGames is down (as usual) and there's no sign of Carlos (as usual).  Until stuff gets fixed, head over to &lt;a href="irc://irc.freenode.net#idevgames"&gt;#idevgames on FreeNode&lt;/a&gt;.  &lt;a href="http://colloquy.info/"&gt;Colloquy&lt;/a&gt; is a good IRC client if you haven't used one before.&lt;/p&gt;&lt;p&gt;Further bulletins as events warrant.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-8823402913962453913?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/SPlKWcngCQk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/8823402913962453913/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=8823402913962453913" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/8823402913962453913?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/8823402913962453913?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/SPlKWcngCQk/idevgames-is-down.html" title="iDevGames is Down" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/12/idevgames-is-down.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkcHRXY_fSp7ImA9WB9VE04.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-9143822232898739574</id><published>2007-11-29T22:45:00.000+13:00</published><updated>2007-11-29T22:53:54.845+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-29T22:53:54.845+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="philosophy" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="languages" /><title>Choosing a Programming Language</title><content type="html">&lt;p&gt;&lt;span style="font-style:italic;"&gt;This article was originally written for &lt;a href="http://idevgames.com/"&gt;iDevGames&lt;/a&gt; and CreateMacGames (no longer in existence), but I'm republishing it here as a complement to my &lt;a href="/2007/11/why-not-c.html"&gt;"Why not C++"&lt;/a&gt; post.  It's not exactly what I'd say today (Haskell's a lot harder to master than I make out, and a lot better at I/O than I make out) but I think it's still a good read.&lt;/span&gt;&lt;/p&gt;&lt;h4&gt;Like Moths to a Naked Flame&lt;/h4&gt;&lt;p&gt;An oft-asked question by beginner programmers is, "What language should I use?".  The answers that are given vary, but usually involve heavy recommendations of C and C++.  And that's where it stops.  The student learns the recommended language, and never stops to consider what else they might learn.&lt;/p&gt;&lt;p&gt;Time passes, the bright-eyed student becomes something of a guru herself, and when it comes their turn to answer the inevitable question for the next generation, she's hemmed in by her own experience.  "C or C++", comes the stern reply, and another generation marches blinkered toward the same fate.&lt;/p&gt;&lt;p&gt;Why must this always be the case?  Why is it so difficult to leave the beaten track?  Why, when it comes time to start a new project, is the question always "How can this be done in C++?", rather than "What language would make this project easier?"?&lt;/p&gt;&lt;h4&gt;Why Computers are Better than People, Reason #457&lt;/h4&gt;&lt;p&gt;The point many people miss, here, is that computer languages aren't like human languages.  It might take you a lifetime to master Japanese.  You're looking at maybe a week for Python.  One week to mastery.  Not "good morning" and "my name is Ellie" clumsy beginnings; not even mere competent conversation; we're talking complete, total and utter mastery.&lt;/p&gt;&lt;p&gt;Pick a difficult language, say Perl, and we might be in two-week territory.&lt;/p&gt;&lt;p&gt;How about a completely different paradigm; a whole new way of thinking?  It shouldn't take a month to master Haskell.&lt;/p&gt;&lt;p&gt;There's no reason to limit yourself by the languages you know — the effort to acquire a new one is miniscule, while the pay-offs can be tremendous.&lt;/p&gt;&lt;h4&gt;On Round Pegs and Square Holes&lt;/h4&gt;&lt;p&gt;Every new project gives the opportunity to start afresh; to reevaluate preconceptions; to apply knowledge gained during the execution of the last.  The choice of a programming language is no different.&lt;/p&gt;&lt;p&gt;The decisions made now will have a profound impact on the outcome of this latest venture.  The choice of language(s) will affect how easy it is to produce the program in the first place; it will affect how easy it is to modify the program later; it may even affect how easy it is for the program's users to customize your program to their liking.  Why ignore the choice given you?&lt;/p&gt;&lt;p&gt;People don't like to ask the difficult questions.  The questions like "Why are we using C (a language for writing high-performance code) to create a desktop office suite (where performance requirements are very low)?".  The questions like "Why are we using Java (a language for writing high-level reusable code with low performance requirements) to write a software 3D renderer supposed to run in real-time?".  The questions like "Why are we using Haskell (a language good for complex mathematical computation, but poor at I/O) to write a file-format translator?".  The questions like "Why are we using Perl (a language good for text-processing) to write an image-editing program?".&lt;/p&gt;&lt;p&gt;Would you fire your builder if she tried to drill a hole in your wall with a spanner?&lt;/p&gt;&lt;h4&gt;Choosing the Right Tool for the Job&lt;/h4&gt;&lt;p&gt;Every language has its strengths and weaknesses; it has areas where it particularly shines, and areas where you'll only be fighting the language to try and accomplish anything.&lt;/p&gt;&lt;p&gt;When you start your next project, stop and think for a while.  List the requirements you have for your programming language, and match them up against actual languages.  Chances are you won't find an exact match, but this should help focus your attention on a small group of languages that are more suited to your task than the others.&lt;/p&gt;&lt;h4&gt;Color-Coordination; or When a Track-Suit isn't Enough&lt;/h4&gt;&lt;p&gt;One problem you've likely encountered having followed the advice of the previous paragraph is that different parts of your program have different requirements.  If you're writing a game, for example, you'll probably have a large portion of your program (containing AI, game logic, user interface, &amp;c.) which needs to be easy to build and easy to change, but where performance isn't critical.  Unfortunately, you'll also have a small, hard core of the functionality (probably graphics and physics routines) where performance is of the utmost importance.&lt;/p&gt;&lt;p&gt;As with clothing, not everything has to be the same color.  As with food, you're allowed to have more than one flavor in a meal.  In programming, you don't have to use the same language throughout the entire program.  There are ways and means of embedding almost all languages in other languages, even if it involves going via C on the way.  This way you can enjoy the benefits of each language in the areas suited to it, without succumbing to its weak areas.&lt;/p&gt;&lt;p&gt;Beware the fashion police, however — too many different languages in one project will quickly tie you up in the mess of integration, rather than letting you get on with using your chosen languages.  I still have visions of my high-school English teacher, whose favorite color combination was yellow, lime green, turquoise and hot pink.  You don't have to be like her!&lt;/p&gt;&lt;h4&gt;Forging a New Path&lt;/h4&gt;&lt;p&gt;Take off your blinkers!  Look around you!  Marvel at the vast array of possibilities!  Dare to find the language which makes your task easier!&lt;/p&gt;&lt;p&gt;And when you, in turn, get asked "Which language should I learn?", do the right thing — ask a question in return: "What do you want to create today?".&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-9143822232898739574?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/v_IBk-9wx5s" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/9143822232898739574/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=9143822232898739574" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/9143822232898739574?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/9143822232898739574?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/v_IBk-9wx5s/choosing-programming-language.html" title="Choosing a Programming Language" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/11/choosing-programming-language.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0cARX46eip7ImA9WB9VE00.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-2504624547781301528</id><published>2007-11-29T15:06:00.000+13:00</published><updated>2007-11-29T17:04:04.012+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-29T17:04:04.012+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gcc" /><category scheme="http://www.blogger.com/atom/ns#" term="bug" /><title>I Broke GCC</title><content type="html">&lt;pre&gt;&lt;code&gt;CookieJar:Desktop keith$ cat test.mm 
template &amp;lt;typename S&amp;gt;
void swap_struct(S *s)
{
    const char *encoding = @encode(S);
    // ...
}
CookieJar:Desktop keith$ gcc test.mm 
test.mm: In function ‘void swap_struct(S*)’:
test.mm:4: internal compiler error: Bus error
Please submit a full bug report,
with preprocessed source if appropriate.
See &amp;lt;URL:http://developer.apple.com/bugreporter&amp;gt; for instructions.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Darn.  That would've been a cool piece of code.  Oh well, a macro will suffice.&lt;/p&gt;&lt;p&gt;Radar 5619052.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-2504624547781301528?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/cz_84JGcAsw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/2504624547781301528/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=2504624547781301528" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/2504624547781301528?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/2504624547781301528?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/cz_84JGcAsw/i-broke-gcc.html" title="I Broke GCC" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/11/i-broke-gcc.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEUDRnY7fip7ImA9WB9VEkU.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-6471729290613016839</id><published>2007-11-29T09:22:00.000+13:00</published><updated>2007-11-29T09:37:57.806+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-29T09:37:57.806+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="c++" /><category scheme="http://www.blogger.com/atom/ns#" term="faq" /><category scheme="http://www.blogger.com/atom/ns#" term="philosophy" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="languages" /><title>Why not C++?</title><content type="html">&lt;p&gt;&lt;span style="font-style:italic;"&gt;This was originally posted on &lt;a href="http://idevgames.com/forum"&gt;iDevGames&lt;/a&gt;, but since people are constantly asking me why I hate C++ so much, I thought it should be posted here too.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;My philosophy is that at any given time, there are three things that you may care about when programming.  From least common to most common:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;You want to write fast code.&lt;/li&gt;&lt;li&gt;You want to write correct code.&lt;/li&gt;&lt;li&gt;You want it to be easy to write code.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Unfortunately, these tend to work against each other.&lt;/p&gt;&lt;p&gt;If you want to write fast code, you need a language which takes you close to the hardware.  It needs to provide a fairly direct mapping between the "high-level" source and the assembly, so that you can pinpoint problems in the assembly and know exactly what needs to change in the high-level code to fix them.  The only language I've ever used which does this is C.  With C++, as soon as you get into references, copy constructors, operator overloading, etc. there is too much going on for that direct correlation between source and assembly to exist.&lt;/p&gt;&lt;p&gt;If you want to write correct code, you need a language which prevents you from making as many errors as possible.  That implies a strict static type system, array bounds checking, an inability to poke around directly at bits and bytes in memory, etc.  Obviously, C++ doesn't fit that bill; the closest language I've found is Haskell.  Java's unchecked exceptions rule it out, though there are tools that can help with that.&lt;/p&gt;&lt;p&gt;If you want it to be easy to write code, you need a language that allows you to express what you want; that doesn't tie you down to rigid typing structures, or keep you fiddling with minutiae like memory management.  This tends to mean a highly dynamic object-oriented language.  Unsurprisingly, since this is the most common category for code to be in, this is the category with the most appropriate languages in it.  Ruby and Python are two obvious ones.  C++ is far too static, and keeps you far too immersed in low-level details to be useful here.&lt;/p&gt;&lt;p&gt;Of course, any given project is likely to consist of parts which fall into each of these three categories, which leads many people to seek a "compromise language" &amp;mdash; one which is "fast enough", "safe enough" and "easy enough".  Ultimately, I believe such languages are always a waste of time.  You will spend more time optimizing the bits that need optimizing, more time fixing bugs in the critical sections of the code, and much, much more time trying to design your software in such a way that you can actually build it, than if you were using the appropriate language for each part.&lt;/p&gt;&lt;p&gt;These languages are not difficult to tie together.  All have easy ways to interact with C, which can be used as a thin layer of glue to stick them all together.  The boundaries between the sections of code with different requirements tend to be reasonably well-defined, and you can therefore avoid crossing your language boundaries too often.&lt;/p&gt;&lt;p&gt;If you're using the right language for the job, your code will be as clear, concise, and well-designed as it's possible for it to be.  To use the favorite vehicle analogy, you wouldn't try to use a Ford Ka as a personnel carrier in a war zone, and you wouldn't drive to the shops to get the groceries in an APC.  Either might "work", but you'd be better off with two separate vehicles.&lt;/p&gt;&lt;p&gt;And please, don't try the argument that "it's easy for me to write C++" or "I don't waste time fighting C++'s memory management and lack of dynamicity" or "I don't make bugs these days"; it's well known that people are notoriously bad at analyzing their own productivity.  There are simple studies and metrics that prove that programmers are more productive in more dynamic languages, and write safer code in safer languages.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-6471729290613016839?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/9eso62DLodg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/6471729290613016839/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=6471729290613016839" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/6471729290613016839?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/6471729290613016839?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/9eso62DLodg/why-not-c.html" title="Why not C++?" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/11/why-not-c.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUcFR30_fCp7ImA9WB9VEE8.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-358765484843802591</id><published>2007-11-26T09:30:00.000+13:00</published><updated>2007-11-26T09:36:56.344+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-26T09:36:56.344+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gcc" /><category scheme="http://www.blogger.com/atom/ns#" term="faq" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>GCC Predefined Macros</title><content type="html">&lt;p&gt;As a follow-up to my &lt;a href="/2007/07/useful-gcc-macros.html"&gt;earlier post&lt;/a&gt; on macros for detecting compilers, OSes and hardware, here's an easy way to find &lt;span style="font-weight:bold;"&gt;all&lt;/span&gt; the macros GCC is predefining for you:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;echo | gcc -dM -E -x objective-c -arch ppc -mmacosx-version-min=10.3 -&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Obviously, substitute the appropriate language, architectures, and minimum Mac OS X version.  The languages are &lt;code&gt;c&lt;/code&gt;, &lt;code&gt;objective-c&lt;/code&gt;, &lt;code&gt;c++&lt;/code&gt; and &lt;code&gt;objective-c++&lt;/code&gt;.  The architectures (as of Leopard) are &lt;code&gt;ppc&lt;/code&gt;, &lt;code&gt;i386&lt;/code&gt;, &lt;code&gt;ppc64&lt;/code&gt; and &lt;code&gt;x86_64&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-358765484843802591?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/9zH6NCdaePI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/358765484843802591/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=358765484843802591" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/358765484843802591?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/358765484843802591?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/9zH6NCdaePI/gcc-predefined-macros.html" title="GCC Predefined Macros" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/11/gcc-predefined-macros.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QMRngzfSp7ImA9WB9WGUg.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-2203823595473669575</id><published>2007-11-25T15:49:00.000+13:00</published><updated>2007-11-25T15:56:27.685+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-25T15:56:27.685+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rant" /><category scheme="http://www.blogger.com/atom/ns#" term="ichat" /><category scheme="http://www.blogger.com/atom/ns#" term="bug" /><title>5MB of logged iChats = ...</title><content type="html">&lt;p&gt;I was looking for a URL, and all I knew was that it was probably sent to me by a particular person on iChat.  I found the iChat logs, found the chats with this person (126 documents, about 5MB on disk) and opened 'em all up.&lt;/p&gt;&lt;p&gt;Bad idea.&lt;/p&gt;&lt;p&gt;When, after about ten minutes, iChat was using 400MB of &lt;code&gt;RPRVT&lt;/code&gt; memory and was still not responsive, I force quit.&lt;/p&gt;&lt;p&gt;Bad idea.&lt;/p&gt;&lt;p&gt;Turns out iChat (on Leopard only?) saves the state of all its documents, and reopens them when it relaunches.  I reopened iChat, and it became instantly unresponsive, doing exactly the same thing.  So, I left it to finish opening all the documents, then option-clicked a close box.  It closed all the windows quite happily, but has been unresponsive since.  If I force quit now, will it reopen all those documents next time I open it?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-2203823595473669575?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/IpmYkM0yLEc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/2203823595473669575/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=2203823595473669575" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/2203823595473669575?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/2203823595473669575?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/IpmYkM0yLEc/5mb-of-logged-ichats.html" title="5MB of logged iChats = ..." /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/11/5mb-of-logged-ichats.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE8ASXw9fyp7ImA9WB9WGU4.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-7731981769701795535</id><published>2007-11-25T09:39:00.000+13:00</published><updated>2007-11-25T09:40:48.267+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-25T09:40:48.267+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rant" /><title>As if Quesa wasn't a bad enough idea...</title><content type="html">&lt;p&gt;&lt;a href="http://lists.apple.com/archives/mac-games-dev/2007/Nov/msg00081.html"&gt;Please kill me now.&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-7731981769701795535?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/H-f4GtLV_IU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/7731981769701795535/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=7731981769701795535" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/7731981769701795535?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/7731981769701795535?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/H-f4GtLV_IU/as-if-quesa-wasnt-bad-enough-idea.html" title="As if Quesa wasn't a bad enough idea..." /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/11/as-if-quesa-wasnt-bad-enough-idea.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE4ARH44fip7ImA9WB9WGEg.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-253299965217492740</id><published>2007-11-23T23:40:00.000+13:00</published><updated>2007-11-24T10:22:25.036+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-24T10:22:25.036+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rant" /><category scheme="http://www.blogger.com/atom/ns#" term="games" /><category scheme="http://www.blogger.com/atom/ns#" term="usability" /><title>Re: Apple and Gaming: being constructive</title><content type="html">&lt;p&gt;&lt;span style="font-style:italic;"&gt;In response to a rapidly-derailed &lt;a href="http://lists.apple.com/archives/mac-games-dev/2007/Nov/msg00024.html"&gt;thread&lt;/a&gt; on the &lt;a href="http://lists.apple.com/mailman/listinfo/mac-games-dev"&gt;mac-games-dev list&lt;/a&gt;.  Everything I was going to say has been said already, but I still feel the need to vent!&lt;/span&gt;&lt;/p&gt;&lt;p&gt;What can Apple do to help Mac game developers?  They can carry on doing exactly what they are.&lt;/p&gt;&lt;p&gt;The Mac is a computer for users.  It always has been, and hopefully it always will be.  That means that in any situation, the user comes first.  It means that developers need to abandon their arrogant Windows ways when they come to the platform, put the users first, and write games that play nicely with the rest of the system.&lt;/p&gt;&lt;p&gt;&lt;span style="font-style:italic;"&gt;A game is just another application.&lt;/span&gt;  It doesn't get special treatment.  It should run off a disk image, install with a drag and drop, store its hidden files in &lt;code&gt;~/Library/&lt;/code&gt;, use the system's concept of user accounts rather than hacking on its own, use as little battery power as possible on laptops, leave the user's windows where they are, and so forth.  These are all non-negotiable parts of being a Macintosh application, and there is absolutely no need for a game to violate them.  If you think you have a good reason why these won't work for you, you don't.  Think again, there's a better solution.&lt;/p&gt;&lt;p&gt;If Apple were to ship a gaming SDK, it would give the false impression that games are somehow special, deserving of extra attention, subject to special exceptions.  They're not.&lt;/p&gt;&lt;p&gt;Games can perform self-update in the same way that &lt;a href="http://sparkle.andymatuschak.org/"&gt;Sparkle&lt;/a&gt;-enabled applications already do.  Games can download content automatically to a location in &lt;code&gt;~/Library/&lt;/code&gt;.  If the game wants to enable a competitive high-score table between user accounts, it can ask for administrator permission to create that table in &lt;code&gt;/Library/Application Support&lt;/code&gt;.  These are things which are well-known to application programmers, and don't occasion complaint.  Why do game developers think they're special?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-253299965217492740?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/O7RZzBovuwY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/253299965217492740/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=253299965217492740" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/253299965217492740?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/253299965217492740?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/O7RZzBovuwY/re-apple-and-gaming-being-constructive.html" title="Re: Apple and Gaming: being constructive" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/11/re-apple-and-gaming-being-constructive.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk4BSXc4fyp7ImA9WxRVFUo.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-4651941374507082037</id><published>2007-11-23T10:07:00.000+13:00</published><updated>2008-11-14T00:15:58.937+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-14T00:15:58.937+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="bug" /><category scheme="http://www.blogger.com/atom/ns#" term="launchservices" /><title>LSMinimumSystemVersion</title><content type="html">&lt;p&gt;I tried using the Info.plist key &lt;code&gt;LSMinimumSystemVersion&lt;/code&gt; to require 10.4.11.  It doesn't work &amp;mdash; even on 10.4.11, you're told you can't do that:&lt;/p&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;" src="http://4.bp.blogspot.com/_cVAaWltDN2c/R0XwsBYcuEI/AAAAAAAAADU/vNpbkr0vKY0/s400/CannotUseWithThisVersion.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5135775589161809986" /&gt;&lt;p&gt;If I change the app to require 10.4.8 instead it works fine.  I'm guessing that LaunchServices is using &lt;code&gt;gestaltSystemVersion&lt;/code&gt; to get the system version, which returns 0x1049 for all of 10.4.9, 10.4.10 and 10.4.11... Perhaps Apple should follow &lt;a href="http://developer.apple.com/documentation/Carbon/Reference/Gestalt_Manager/Reference/reference.html#//apple_ref/doc/c_ref/gestaltSystemVersion"&gt;their own advice&lt;/a&gt; about retrieving the system version...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-4651941374507082037?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/bZc18bcJiCg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/4651941374507082037/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=4651941374507082037" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/4651941374507082037?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/4651941374507082037?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/bZc18bcJiCg/lsminimumsystemversion.html" title="LSMinimumSystemVersion" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_cVAaWltDN2c/R0XwsBYcuEI/AAAAAAAAADU/vNpbkr0vKY0/s72-c/CannotUseWithThisVersion.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/11/lsminimumsystemversion.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcMQX4-eCp7ImA9WB9WF00.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-4285129672659912221</id><published>2007-11-22T16:50:00.000+13:00</published><updated>2007-11-22T17:01:20.050+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-22T17:01:20.050+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="universal binary" /><category scheme="http://www.blogger.com/atom/ns#" term="gcc" /><category scheme="http://www.blogger.com/atom/ns#" term="autotools" /><category scheme="http://www.blogger.com/atom/ns#" term="faq" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Universal Binaries from Autotool'd Projects</title><content type="html">&lt;p&gt;A frequent question I get is "how do I build a universal binary from an autotool'd project?".  Unfortunately, as with all things autotools, the answer isn't quite straightforward.  The best approach is to configure and make the project multiple times, once for each architecture you want in your UB, with different compiler settings each time.&lt;/p&gt;&lt;p&gt;Since autotool'd projects vary widely, the details for each will differ, but at the least, you'll need to set the &lt;code&gt;CFLAGS&lt;/code&gt; environment variable appropriately (eg. &lt;code&gt;-arch ppc -isysroot /Developer/SDKs/MacOSX10.3.9.sdk -mmacosx-version-min=10.3&lt;/code&gt;), and pass the correct host for a cross-compile to configure (eg. &lt;code&gt;.configure --prefix=`pwd`/build/ppc --host=powerpc-apple-darwin7&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;Once you've got a version for each architecture built, you can use &lt;code&gt;lipo&lt;/code&gt; to join them together into a UB.&lt;/p&gt;&lt;p&gt;If all that sounds like too much effort, my &lt;a href="http://onesadcookie.com/svn/Third-Party"&gt;"Third-Party"&lt;/a&gt; project does it already for a few common open-source libraries, and the infrastructure there makes it easy to add more.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-4285129672659912221?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/LpyU3YLxtkE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/4285129672659912221/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=4285129672659912221" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/4285129672659912221?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/4285129672659912221?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/LpyU3YLxtkE/universal-binaries-from-autotoold.html" title="Universal Binaries from Autotool'd Projects" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/11/universal-binaries-from-autotoold.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUEFR3s_eyp7ImA9WB9WFkk.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-4665407268006053363</id><published>2007-11-22T00:09:00.001+13:00</published><updated>2007-11-22T00:13:36.543+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-22T00:13:36.543+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="intel" /><category scheme="http://www.blogger.com/atom/ns#" term="glsl" /><category scheme="http://www.blogger.com/atom/ns#" term="kiloplane" /><category scheme="http://www.blogger.com/atom/ns#" term="bug" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="x3100" /><category scheme="http://www.blogger.com/atom/ns#" term="opengl" /><title>More Kiloplane Bugs</title><content type="html">&lt;p&gt;Finally, 10.5 comes out, and at last the GLSL rendering bugs that have plagued &lt;a href="http://onesadcookie.com/svn/Kiloplane"&gt;Kiloplane&lt;/a&gt; seem to be fixed across the board.  Celebrations all round.&lt;/p&gt;&lt;p&gt;Today, Ellie got a new MacBook (the first Mac with an Intel GMA X3100), and I got to test Kiloplane there.  Guess what?  Modes 3 and 4 render incorrectly.  Sigh.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-4665407268006053363?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/L2l-30eIbSI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/4665407268006053363/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=4665407268006053363" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/4665407268006053363?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/4665407268006053363?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/L2l-30eIbSI/more-kiloplane-bugs.html" title="More Kiloplane Bugs" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/11/more-kiloplane-bugs.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0IBQnc-fip7ImA9WB9WFk8.&quot;"><id>tag:blogger.com,1999:blog-4238494280010469357.post-4943306172822549286</id><published>2007-11-20T20:27:00.000+13:00</published><updated>2007-11-21T20:19:13.956+13:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-11-21T20:19:13.956+13:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="launchservices" /><title>Info.plist Keys</title><content type="html">&lt;p&gt;Apple has a &lt;a href="http://developer.apple.com/documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/PListKeys.html"&gt;list of Info.plist keys&lt;/a&gt;.  There's a bunch there that I didn't know about that look useful, in particular &lt;code&gt; ATSApplicationFontsPath&lt;/code&gt; and &lt;code&gt; LSMinimumSystemVersion(ByArchitecture)&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238494280010469357-4943306172822549286?l=blog.onesadcookie.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/OneSadCookie/~4/reOfm1oUQCE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.onesadcookie.com/feeds/4943306172822549286/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=4238494280010469357&amp;postID=4943306172822549286" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/4943306172822549286?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4238494280010469357/posts/default/4943306172822549286?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/OneSadCookie/~3/reOfm1oUQCE/infoplist-keys.html" title="Info.plist Keys" /><author><name>OneSadCookie</name><uri>http://www.blogger.com/profile/04354982399760362605</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="06012716005256281167" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.onesadcookie.com/2007/11/infoplist-keys.html</feedburner:origLink></entry></feed>
