{"id":5620,"date":"2022-04-03T21:02:43","date_gmt":"2022-04-04T04:02:43","guid":{"rendered":"https:\/\/blackcap.name\/blog\/new\/?p=5620"},"modified":"2022-04-14T21:12:42","modified_gmt":"2022-04-15T04:12:42","slug":"making-gui-apps-for-linux-macintosh-and-windows","status":"publish","type":"post","link":"https:\/\/blackcap.name\/blog\/new\/?p=5620","title":{"rendered":"Making GUI Apps for Linux, Macintosh, and Windows"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">Foreword<\/h3>\n\n\n\n<p>I collected this information about two years ago, intending to publish it here, yet never did. Before I lost track of it, I decided to do so this evening. Note that this information was correct <em>as of two years ago<\/em>; so there is a chance that some things have changed since then. (Though I am at present revisiting these topics, and so far I haven&#8217;t found any changes in what follows.)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Introduction<\/h3>\n\n\n\n<p>I\u2019ve been mostly a back-end programmer and command-line guy. That, plus inertia, has caused me to not bother with supporting graphical user interfaces in the code I write.<\/p>\n\n\n\n<p>Until recently, that is. There\u2019s a few ideas I\u2019ve had rattling around in my head that would be useful for others, but many of those other people are not computer geeks and would not be interested in opening a Terminal or Command Prompt app just to run my command-line programs.<\/p>\n\n\n\n<p>As a result, I\u2019ve been learning how to make normal, \u201cclickable\u201d apps that a normal person would be able to run without extensive training and hand-holding. Might as well share that knowledge with others.<\/p>\n\n\n\n<p>What follows is likely to be particularly useful to those who, like me, are using something <em>other than<\/em> the normal, approved tools to code their apps. In my case, I\u2019m using <a href=\"https:\/\/kotlinlang.org\/\">Kotlin<\/a>, because it\u2019s a modern language with a powerful, expressive syntax and it runs under the Java virtual machine, making it much easier to port my code to different systems.<\/p>\n\n\n\n<p>Yes, there are \u201cstandard\u201d programs to do all of the following. Without very few exceptions, I have found them to be poorly documented and geared to C\/C++ developers. I found that attempting to bend these tools to my will was sufficiently difficult and painful that it was easier to forget about them and just do it all myself, mostly from scratch.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Linux (Gnome on Ubuntu)<\/h3>\n\n\n\n<p>To have an application that comes up as a clickable icon like all the other normal Gnome apps, one must install files in a number of places, primarily under <code>\/usr\/share<\/code>. It\u2019s something of a mess, as the files that define the presence of a given app are scattered here and there, not collected in one place as they are on a Mac.<\/p>\n\n\n\n<p>The easiest way to cope with this state of affairs is to do what everyone else does: make a Debian package (Ubuntu is Debian under the hood). Thankfully, <a href=\"https:\/\/manpages.debian.org\/unstable\/dpkg-dev\/deb.5.en.html\"><code>.deb<\/code> files are pretty simple<\/a>: an <code>ar<\/code> archive with three members:<\/p>\n\n\n<dl>\n<dt><code>debian-binary<\/code><\/dt>\n<dd>This consists of the string \u201c2.0\u201d followed by a newline. That\u2019s it. It must be the first member of the archive.<\/dd>\n<dt><code>control.tar.gz<\/code><\/dt>\n<dd>Files that control the installation. The <code>control<\/code> (yes, <code>control.tar.gz<\/code> contains a file named <code>control<\/code>) file is the only mandatory one, though an <code>md5sums<\/code> one is highly recommended. This must be the second member of the archive.<\/dd>\n<dt><code>data.tar.gz<\/code><\/dt>\n<dd>The files to install. They all are relative paths (to root). I.e. if your package has an executable to be installed as <code>\/usr\/share\/program<\/code> it will be in here as <code>usr\/share\/program<\/code>. This must be the third and final member of the archive.<\/dd>\n<\/dl>\n\n\n<p>Note that <em>all files must be stored with the correct ownership information<\/em> (usually as <code>root<\/code>, the superuser).<\/p>\n\n\n\n<p>In order for a Linux application to look like a normal, clickable Gnome app, and for <code>lintian<\/code> to not complain a lot about your package, you need to have a number of files installed (i.e. present in <code>data.tar.gz<\/code>). For the purpose of this list, <em>name<\/em> refers to your application\u2019s name, folded to lower case:<\/p>\n\n\n<dl>\n<dt><code>usr\/bin\/<\/code><i>name<\/i><\/dt>\n<dd>Most clickable apps on Ubuntu are also installed so that they may be invoked via the command line.<\/dd>\n<dt><code>usr\/share\/applications\/<\/code><i>name<\/i><code>.desktop<\/code><\/dt>\n<dd>This is the main file whose presence enables your application to show up on the desktop.<\/dd>\n<dt><code>usr\/share\/doc\/<\/code><i>name<\/i><code>\/changelog.gz<\/code><\/dt>\n<dd>A description of the changes made to the package. This is compressed from a file that has a nasty, column-sensitive format; it is described in detail below.<\/dd>\n<dt><code>usr\/share\/doc\/<\/code><i>name<\/i><code>\/copyright<\/code><\/dt>\n<dd>A copyright message. If it refers to one of the standard licenses described in <code>\/usr\/share\/common-licenses<\/code>, you should not include the full license terms in this file but instead end it with a reference to the common license.<\/dd>\n<dt><code>usr\/share\/icons\/hicolor\/48x48\/apps\/<\/code><i>name<\/i><code>.png<\/code><\/dt>\n<dd>At a minimum, you must define 48 \u00d7 48 an icon in the <span style=\"font-family: monospace;\">hicolor<\/span><code><\/code> theme. The name of this icon file must match the name of the icon described in the <code>.desktop<\/code> file; for sanity\u2019s sake, just use your application name.<\/dd>\n<dt><code>usr\/share\/<\/code><em>name<\/em><\/dt>\n<dd>If your program has any data files, these go in this directory. For example, the <code>.jar<\/code> and <code>.class<\/code> files for a Java application will live here.<\/dd>\n<dt><code>usr\/share\/man\/man1\/<\/code><em>name<\/em><code>.1.gz<\/code><\/dt>\n<dd>There should be a manual page for your application, which should be in compressed form.<\/dd>\n<\/dl>\n\n\n<p>It is best to use <code>dpkg<\/code> to inspect a few <code>.deb<\/code> files for programs similar to yours to get an idea of what you need to define. A good source of such files can be the system package cache, <code>\/var\/cache\/apt\/arhives<\/code>.<\/p>\n\n\n\n<p>Once you have a directory tree with the files you need, it is a simple enough matter to use <code>tar<\/code> and <code>ar<\/code> to create a <code>.deb<\/code> file:<\/p>\n\n\n<pre><code>echo 2.0 &gt; debian-binary\ncd data\nfind * -type f -print0 | xargs -0 md5sum &gt; ..\/control\/md5sums\ntar -c -z --owner=0 --group=0 -f ..\/data.tar.gz *\ncd ..\/control\ntar -c -z --owner=0 --group=0 -f ..\/control.tar.gz *\ncd ..\nar r name.deb debian-binary control.tar.gz data.tar.gz<\/code><\/pre>\n\n\n<p>After creating your .deb package, it is strongly recommended that you use <code>lintian --info<\/code> to check it. In general, you should be concerned about anything flagged at the <code>E<\/code> (error) level, and at least make an effort to reduce the number of <code>W<\/code> (warning) level messages that <code>lintian<\/code> reports.<\/p>\n\n\n\n<p>One thing I <em>don\u2019t<\/em> worry about is complaints from <code>lintian<\/code> about the manual pages not being compressed to the maximum level: I use an Apache Ant task, not the <code>gzip<\/code> utility, to generate my compressed files, and it has no option to select maximum compression. It makes no difference to the system, and the amount of space saved by using maximum compression over the normal level is insignificant.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">The Changelog File<\/h4>\n\n\n\n<p>Welcome to the year 1967. You are at a keypunch machine preparing a data deck for an IBM 360 program. Be sure to follow the correct rules for which records get punched starting in which column, or you will be rewarded with lots of error codes <code>IEBLINTIAN<\/code> later!<\/p>\n\n\n\n<p>Your changelog deck consists of a series of cards, which contains groups of records which describe changes made to your program. Each group of records must begin with a card punched starting in column 1, of the following format:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>name<\/em> <strong>(<\/strong><em>version<\/em><strong>)<\/strong> <em>distribution<\/em><strong>; urgency=<\/strong><em>urgency<\/em><\/code><\/pre>\n\n\n\n<p>Things in bold should be typed verbatim; things in italics should be replaced with something appropriate:<\/p>\n\n\n<dl>\n<dt><i>name<\/i><\/dt>\n<dd>The name of this package, consistent with the name used elsewhere<\/dd>\n<dt><i>version<\/i><\/dt>\n<dd>Pretty obvious. The version mumber.<\/dd>\n<dt><i>distribution<\/i><\/dt>\n<dd>Until and unless your package becomes a well-established part of the core distribution, this should probably be <code>unstable<\/code>.<\/dd>\n<dt><em>urgency<\/em><\/dt>\n<dd>This will usually be <code>low<\/code>.<\/dd>\n<\/dl>\n\n\n<p>Following this card, you may optionally have a blank card. The details of the change are introduced by a card with an asterisk punched in column three (columns one and two must be blank); the remaining columns on this card describe the change. If describing the change takes more than a single card, subsequent continuation cards are punched starting in column five.<\/p>\n\n\n\n<p>After the description comes an optional blank card, followed by the card defining the programmer and date. This is done by punching hyphens in columns 2 and 3; the card has the following overall format:<\/p>\n\n\n<pre><code>  -- Joe Coder &lt;joe@coder.com&gt;  Thu, 30 Apr 2020 13:16:43 -0700<\/code><\/pre>\n\n\n<p>Note that the e-mail address must be in angle brackets, and there must be <em>two<\/em> blanks separating it from the date, which must be punched in RFC2822 format (the same as reported by the <code>date -R<\/code> command).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Macintosh<\/h3>\n\n\n\n<p>A Macintosh application (technically, an \u201c<a href=\"https:\/\/developer.apple.com\/library\/archive\/documentation\/CoreFoundation\/Conceptual\/CFBundles\/BundleTypes\/BundleTypes.html\">application bundle<\/a>\u201d) is actually a directory whose name ends in <code>.app<\/code> and which contains but a single subdirectory, <code>Contents<\/code>. That subdirectory in turn must contain a number of files:<\/p>\n\n\n<dl>\n<dt><code>Contents\/Info.plist<\/code><\/dt>\n<dd>This is an XML document in a specific form, which is described <a href=\"https:\/\/developer.apple.com\/library\/archive\/documentation\/General\/Reference\/InfoPlistKeyReference\/Articles\/AboutInformationPropertyListFiles.html\">here<\/a>. If you are developing a Java application, see \u201c<code>Info.plist<\/code> Java Notes\u201d below. Apple provides a command-line program, <code>\/usr\/libexec\/PlistBuddy<\/code>, which can be useful when generating or reading this file.<\/dd>\n<dt><code>Contents\/MacOS\/<\/code><em>something<\/em><\/dt>\n<dd>This is the executable file for your program. It is OK for it to be an executable script in one of the standard MacOS scripting languages (e.g. a bash script). Its name much match whatever <em>something<\/em> you chose to associate with the <code>CFBundleExecutable<\/code> key in <code>Info.plist<\/code>.<\/dd>\n<dt><code>Contents\/PkgInfo<\/code><\/dt>\n<dd>I have not been able to find much information on this file, but I believe it helps the Mac associate this application with certain file types. In the general case (no special associations required) it suffices to set the contents of this file to <code>APPL????<\/code> .<\/dd>\n<dt><code>Contents\/Resources\/<\/code><em>something<\/em><code>.icns<\/code><\/dt>\n<dd>The application\u2019s icons. See \u201cPreparing Icons\u201d below for more details on how to create this file. This file\u2019s name must match whatever <em>something<\/em> you chose to associate with the <code>CFBundleIconFile<\/code> key in <code>Info.plist<\/code>.<\/dd>\n<\/dl>\n\n\n<p>That\u2019s it for the mandatory contents. Any directory whose name ends in .app and contains the above structure should be recognized as a clickable application by the Macintosh. It is, of course, common for applications to contain read-only data, which is also contained inside the app bundle. For example, <code>.jar<\/code> and <code>.class<\/code> files for Java applications can be stored in <code>Contents\/Java<\/code>. Applications can also use non-reserved keys in <code>Info.plist<\/code> to store configuration information and other data.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Info.plist Java Notes<\/h4>\n\n\n\n<p>Apple has a standard way of storing Java-specific information in <code>Info.plist<\/code>, under a <code>Java<\/code> element in the top-level dictionary. Unfortunately, using it will cause a Mac to attempt to run the app using a very old, Apple-customized Java runtime that isn\u2019t even present on most Macs. Your users will see a dialog with a bunch of blather about the \u201clegacy Java runtime\u201d being needed, and even if they follow Apple\u2019s suggestion and download that runtime, it is likely that won\u2019t be able to run your application, because it is so obsolete.<\/p>\n\n\n\n<p>Therefore, as Groucho Marx said about the doctor\u2019s response to the patient who complained \u201cIt hurts when I do this,\u201d don\u2019t do that. I follow the Oracle convention of putting the entry-point class in <code>JVMMainClassName<\/code>, a package-relative path to my Jar file in <code>JVMClassPath<\/code>, a Java version specification in <code>JVMVersion<\/code>, and any extra options to pass to the Java environment in a <code>JVMOptions<\/code> array.<\/p>\n\n\n\n<p>Actually, it doesn\u2019t much matter. I could have come up with unique keys of my own (so long as they didn\u2019t clash with any official Apple ones), and it would have worked just as well. MacOS is blissfully unaware of the significance of those <code>JVM\u2026<\/code> tags, and simply ignores them. They are meaningful only to my defined <code>CFBundleExecutable<\/code> script, which has been coded to look in <code>Info.plist<\/code> for some of its options.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Preparing Icons<\/h4>\n\n\n\n<p>Mac application icons are stored in <code>.icns<\/code> files. These are actually a collection of multiple icons, defined for a variety of sizes. To create such a file, you must create a directory whose name ends in <code>.iconset<\/code>, and populate it with PNG images containing your icon in various sizes, as described <a href=\"https:\/\/developer.apple.com\/library\/archive\/documentation\/GraphicsAnimation\/Conceptual\/HighResolutionOSX\/Optimizing\/Optimizing.html\">here<\/a>. Then use <code>iconutil<\/code> to generate a <code>.icns<\/code> file; assuming your directory was named <em>name<\/em>.<code>iconset<\/code>, you would type:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>iconutil -c icns <\/code><em>name<\/em><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Windows<\/h3>\n\n\n\n<p>A clickable app on Windows is simply an executable (<code>.EXE<\/code>) file that contains an embedded icon which Windows will recognize and display.<\/p>\n\n\n\n<p>If you create a jar file and set Main-Class in its manifest to point to the class containing your application\u2019s entry point, Windows considers it to be an \u201cexecutable jar\u201d and will launch your application when you click on the jar file. That\u2019s <em>almost<\/em> as good as a having a proper executable with an embedded icon, but it doesn\u2019t have an embedded icon, so your app will display using the default icon that all jar files get.<\/p>\n\n\n\n<p>The solution is to install <a href=\"http:\/\/launch4j.sourceforge.net\/\">Launch4j<\/a> and use it to create a Windows executable with your icon of choice embedded in it. This is a free program, and I have found it to be well-documented.<\/p>\n\n\n\n<p>If your application is sufficiently complex as to require a bunch of support files under Windows, then you will need to create a Microsoft Installer file. My apps have so far been simple enough not to require this, so I don\u2019t have much help to offer in this regard yet.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Foreword I collected this information about two years ago, intending to publish it here, yet never did. Before I lost track of it, I decided to do so this evening. Note that this information was correct as of two years ago; so there is a chance that some things have changed since then. (Though I [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-5620","post","type-post","status-publish","format-standard","hentry","category-computers"],"_links":{"self":[{"href":"https:\/\/blackcap.name\/blog\/new\/index.php?rest_route=\/wp\/v2\/posts\/5620","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blackcap.name\/blog\/new\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blackcap.name\/blog\/new\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blackcap.name\/blog\/new\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blackcap.name\/blog\/new\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5620"}],"version-history":[{"count":18,"href":"https:\/\/blackcap.name\/blog\/new\/index.php?rest_route=\/wp\/v2\/posts\/5620\/revisions"}],"predecessor-version":[{"id":5648,"href":"https:\/\/blackcap.name\/blog\/new\/index.php?rest_route=\/wp\/v2\/posts\/5620\/revisions\/5648"}],"wp:attachment":[{"href":"https:\/\/blackcap.name\/blog\/new\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5620"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blackcap.name\/blog\/new\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5620"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blackcap.name\/blog\/new\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5620"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}