r484 - in trunk: . examples/duke3d/scripts examples/ut3-dedicated/scripts scripts

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Wed Feb 13 03:52:25 EST 2008


Author: icculus
Date: 2008-02-13 03:52:23 -0500 (Wed, 13 Feb 2008)
New Revision: 484

Modified:
   trunk/docs.txt
   trunk/examples/duke3d/scripts/config.lua
   trunk/examples/ut3-dedicated/scripts/config.lua
   trunk/lua_glue.c
   trunk/platform.h
   trunk/platform_unix.c
   trunk/scripts/config.lua
   trunk/scripts/localization.lua
   trunk/scripts/mojosetup_init.lua
   trunk/scripts/mojosetup_mainline.lua
Log:
First shot at desktop menu items.


Modified: trunk/docs.txt
===================================================================
--- trunk/docs.txt	2008-02-12 19:59:30 UTC (rev 483)
+++ trunk/docs.txt	2008-02-13 08:52:23 UTC (rev 484)
@@ -182,6 +182,20 @@
 
   Setup.Package attributes:
 
+   vendor (no default, mustExist, mustBeString, cantBeEmpty)
+
+    This is a unique identifier for your organization. This should be in the
+    format "companyname.dom" ... the hope is that your primary website is
+    a unique identifier for your company. If your website is www.icculus.org,
+    you'd enter your vendor setting as "icculus.org" ... just the hostname and
+    top-level domain. This is used largely by the OS to maintain packages
+    (Mac OS X application bundles, vendor IDs for Unix desktop menus, etc).
+    This is, in theory, never shown to the user, so you don't need this to
+    actually exist as a website with meaningful content, so long as you can
+    reasonably assure that the string is unique and follows the "host.dom"
+    format.
+
+
    id (no default, mustExist, mustBeString, cantBeEmpty)
 
     This is a unique identifier for your package. Currently it is used as

Modified: trunk/examples/duke3d/scripts/config.lua
===================================================================
--- trunk/examples/duke3d/scripts/config.lua	2008-02-12 19:59:30 UTC (rev 483)
+++ trunk/examples/duke3d/scripts/config.lua	2008-02-13 08:52:23 UTC (rev 484)
@@ -22,6 +22,7 @@
 
 Setup.Package
 {
+    vendor = "icculus.org",
     id = "duke3d",
     description = "Duke Nukem 3D",
     version = "1.5",

Modified: trunk/examples/ut3-dedicated/scripts/config.lua
===================================================================
--- trunk/examples/ut3-dedicated/scripts/config.lua	2008-02-12 19:59:30 UTC (rev 483)
+++ trunk/examples/ut3-dedicated/scripts/config.lua	2008-02-13 08:52:23 UTC (rev 484)
@@ -3,6 +3,7 @@
 
 Setup.Package
 {
+    vendor = "epicgames.com",
     id = "ut3-dedicated",
     description = "Unreal Tournament 3 Dedicated Server",
     version = "3487",

Modified: trunk/lua_glue.c
===================================================================
--- trunk/lua_glue.c	2008-02-12 19:59:30 UTC (rev 483)
+++ trunk/lua_glue.c	2008-02-13 08:52:23 UTC (rev 484)
@@ -1057,6 +1057,20 @@
 } // luahook_platform_mkdir
 
 
+static int luahook_platform_installdesktopmenuitem(lua_State *L)
+{
+    const char *data = luaL_checkstring(L, 1);
+    return retvalBoolean(L, MojoPlatform_installDesktopMenuItem(data));
+} // luahook_platform_installdesktopmenuitem
+
+
+static int luahook_platform_uninstalldesktopmenuitem(lua_State *L)
+{
+    const char *data = luaL_checkstring(L, 1);
+    return retvalBoolean(L, MojoPlatform_uninstallDesktopMenuItem(data));
+} // luahook_platform_uninstalldesktopmenuitem
+
+
 static int luahook_movefile(lua_State *L)
 {
     boolean retval = false;
@@ -1669,6 +1683,8 @@
             set_cfunc(luaState, luahook_platform_isfile, "isfile");
             set_cfunc(luaState, luahook_platform_symlink, "symlink");
             set_cfunc(luaState, luahook_platform_mkdir, "mkdir");
+            set_cfunc(luaState, luahook_platform_installdesktopmenuitem, "installdesktopmenuitem");
+            set_cfunc(luaState, luahook_platform_uninstalldesktopmenuitem, "uninstalldesktopmenuitem");
         lua_setfield(luaState, -2, "platform");
 
         // Set the GUI functions...

Modified: trunk/platform.h
===================================================================
--- trunk/platform.h	2008-02-12 19:59:30 UTC (rev 483)
+++ trunk/platform.h	2008-02-13 08:52:23 UTC (rev 484)
@@ -231,6 +231,20 @@
 //  the browser will inform the user if there's a problem loading the URL.
 boolean MojoPlatform_launchBrowser(const char *url);
 
+// Add a menu item to the Application menu or Start bar or whatever.
+//  (data) is 100% platform dependent right now, and this interface will
+//  likely change as we come to understand various systems' needs better.
+//  On Unix, it expects this to be a path to a FreeDesktop .desktop file.
+// Returns (true) on success and (false) on failure.
+boolean MojoPlatform_installDesktopMenuItem(const char *data);
+
+// Remove a menu item from the Application menu or Start bar or whatever.
+//  (data) is 100% platform dependent right now, and this interface will
+//  likely change as we come to understand various systems' needs better.
+//  On Unix, it expects this to be a path to a FreeDesktop .desktop file.
+// Returns (true) on success and (false) on failure.
+boolean MojoPlatform_uninstallDesktopMenuItem(const char *data);
+
 #if !SUPPORT_MULTIARCH
 #define MojoPlatform_switchBin(img, len)
 #else

Modified: trunk/platform_unix.c
===================================================================
--- trunk/platform_unix.c	2008-02-12 19:59:30 UTC (rev 483)
+++ trunk/platform_unix.c	2008-02-13 08:52:23 UTC (rev 484)
@@ -26,7 +26,7 @@
 #include <syslog.h>
 #include <dirent.h>
 #include <fcntl.h>
-#include <wait.h>
+#include <sys/wait.h>
 
 #if MOJOSETUP_HAVE_SYS_UCRED_H
 #  ifdef MOJOSETUP_HAVE_MNTENT_H
@@ -1063,28 +1063,47 @@
 } // shellEscape
 
 
-static boolean unix_launchBrowser(const char *url)
+static boolean unix_launchXdgUtil(const char *util, const char **argv)
 {
     boolean retval = false;
-    char *path = findBinaryInPath("xdg-open");
+    char *path = findBinaryInPath(util);
 
     if (path != NULL)  // it's installed on the system; use that.
     {
-        char *escapedurl = shellEscape(url);
-        char *cmd = format("xdg-open %0 >/dev/null 2>&1", escapedurl);
+        char *cmd = path;
+        char *tmp = NULL;
+        int i;
+        for (i = 0; argv[i]; i++)
+        {
+            char *escaped = shellEscape(argv[i]);
+            tmp = format("%0 %1", cmd, escaped);
+            free(escaped);
+            free(cmd);
+            cmd = tmp;
+        } // for
+
+        tmp = format("%0 >/dev/null 2>&1", cmd);
+        free(cmd);
+        cmd = tmp;
         retval = (system(cmd) == 0);
         free(cmd);
-        free(escapedurl);
-        free(path);
     } // if
 
     else  // try our fallback copy of xdg-utils in GBaseArchive?
     {
-        const char *argv[] = { url, NULL };
-        retval = (runScript("meta/xdg-utils/xdg-open", true, argv) == 0);
+        char *script = format("meta/xdg-utils/%0", util);
+        retval = (runScript(script, true, argv) == 0);
+        free(script);
     } // if
 
     return retval;
+} // unix_launchXdgUtil
+
+
+static boolean unix_launchBrowser(const char *url)
+{
+    const char *argv[] = { url, NULL };
+    return unix_launchXdgUtil("xdg-open", argv);
 } // unix_launchBrowser
 #endif
 
@@ -1106,6 +1125,32 @@
 } // MojoPlatform_launchBrowser
 
 
+boolean MojoPlatform_installDesktopMenuItem(const char *data)
+{
+#if PLATFORM_MACOSX || PLATFORM_BEOS
+    // !!! FIXME: write me.
+    STUBBED("desktop menu support");
+    return false;
+#else
+    const char *argv[] = { "install", data, NULL };
+    return unix_launchXdgUtil("xdg-desktop-menu", argv);
+#endif
+} // MojoPlatform_installDesktopMenuItem
+
+
+boolean MojoPlatform_uninstallDesktopMenuItem(const char *data)
+{
+#if PLATFORM_MACOSX || PLATFORM_BEOS
+    // !!! FIXME: write me.
+    STUBBED("desktop menu support");
+    return false;
+#else
+    const char *argv[] = { "uninstall", data, NULL };
+    return unix_launchXdgUtil("xdg-desktop-menu", argv);
+#endif
+} // MojoPlatform_uninstallDesktopMenuItem
+
+
 #if SUPPORT_MULTIARCH
 void MojoPlatform_switchBin(const uint8 *img, size_t len)
 {

Modified: trunk/scripts/config.lua
===================================================================
--- trunk/scripts/config.lua	2008-02-12 19:59:30 UTC (rev 483)
+++ trunk/scripts/config.lua	2008-02-13 08:52:23 UTC (rev 484)
@@ -35,6 +35,7 @@
 
 Setup.Package
 {
+    vendor = "com.mycompany",
     id = "mygame",
     description = "My Game",
     version = "1.0",
@@ -131,6 +132,19 @@
             end
         },
 
+        Setup.DesktopMenuItem
+        {
+            disabled = false,
+            name = "My Game",
+            genericname = "Shoot-em up",
+            comment = "A game for shooting aliens.",
+            builtin_icon = false,
+            icon = "icon.png",  -- relative to the dest; you must install it!
+            commandline = "command-line",
+            categories = "Game",
+            mimetype = { 'application/x-mygame-map', 'application/x-mygame-url' },
+        },
+
         -- Here's an option that has it's own EULA.
         Setup.Option
         {

Modified: trunk/scripts/localization.lua
===================================================================
--- trunk/scripts/localization.lua	2008-02-12 19:59:30 UTC (rev 483)
+++ trunk/scripts/localization.lua	2008-02-13 08:52:23 UTC (rev 484)
@@ -303,6 +303,13 @@
         nb = "FEIL: write_manifest krever støtte for Lua-parser",
     };
 
+    -- This is shown if the config file wants us to add desktop menu items
+    --  but uninstaller support isn't enabled. This is considered bad taste
+    --  to add system menu items without a way to remove them. This is
+    --  a bug the developer must fix before shipping her installer.
+    ["BUG: Setup.DesktopMenuItem requires support_uninstall"] = {
+    };
+
     -- This is a file's permissions. Programmers give these as strings, and
     --  if one isn't valid, the program will report this. So, on Unix, they
     --  might specify "0600" as a valid string, but "sdfksjdfk" wouldn't be
@@ -1014,6 +1021,18 @@
     ["[Make the window taller!]"] = {
         nb = "[Gjør vinduet høyere!]",
     };
+
+    -- This is written out if we failed to add an item to the desktop
+    --  application menu (or "Start" bar on Windows, or maybe the Dock on
+    --  Mac OS X, etc).
+    ["Failed to install desktop menu item"] = {
+    };
+
+    -- This is written out if we failed to remove an item from the desktop
+    --  application menu (or "Start" bar on Windows, or maybe the Dock on
+    --  Mac OS X, etc).
+    ["Failed to uninstall desktop menu item"] = {
+    };
 };
 
 -- end of localization.lua ...

Modified: trunk/scripts/mojosetup_init.lua
===================================================================
--- trunk/scripts/mojosetup_init.lua	2008-02-12 19:59:30 UTC (rev 483)
+++ trunk/scripts/mojosetup_init.lua	2008-02-13 08:52:23 UTC (rev 484)
@@ -245,6 +245,7 @@
 
     tab = sanitize("Package", tab,
     {
+        { "vendor", nil, mustExist, mustBeString, cantBeEmpty },
         { "id", nil, mustExist, mustBeString, cantBeEmpty },
         { "disabled", nil, mustBeBool },
         { "description", nil, mustExist, mustBeString, cantBeEmpty },
@@ -406,6 +407,20 @@
     })
 end
 
+function Setup.DesktopMenuItem(tab)
+    return sanitize("DesktopMenuItem", tab,
+    {
+        { "disabled", nil, mustBeBool },
+        { "name", nil, mustExist, mustBeString, cantBeEmpty },
+        { "genericname", nil, mustExist, mustBeString, cantBeEmpty },
+        { "comment", nil, mustExist, mustBeString, cantBeEmpty },
+        { "builtin_icon", nil, mustBeBool },
+        { "icon", nil, mustExist, mustBeString, cantBeEmpty },
+        { "commandline", nil, mustExist, mustBeString, cantBeEmpty },
+        { "category", nil, mustExist, mustBeStringOrTableOfStrings },
+        { "mimetype", nil, mustBeStringOrTableOfStrings },
+    })
+end
 
 local function prepare_localization()
     -- Map some legacy language identifiers into updated equivalents.

Modified: trunk/scripts/mojosetup_mainline.lua
===================================================================
--- trunk/scripts/mojosetup_mainline.lua	2008-02-12 19:59:30 UTC (rev 483)
+++ trunk/scripts/mojosetup_mainline.lua	2008-02-13 08:52:23 UTC (rev 484)
@@ -125,6 +125,24 @@
 end
 
 
+local function flatten_list(list)
+    local retval = list
+    if type(list) == "table" then
+        retval = ''
+        local first = true
+        for i,v in ipairs(list) do
+            if first then
+                retval = v
+                first = false
+            else
+                retval = retval .. ';' .. v
+            end
+        end
+    end
+    return retval
+end
+
+
 local function do_delete(fname)
     local retval = false
     if fname == nil then
@@ -228,21 +246,7 @@
     -- (The real revertinstall is set later. This is a stub for startup.)
 end
 
-local function real_revertinstall()
-    if MojoSetup.gui_started then
-        MojoSetup.gui.final(_("Incomplete installation. We will revert any changes we made."))
-    end
 
-    MojoSetup.loginfo("Cleaning up half-finished installation...")
-
-    -- !!! FIXME: callbacks here.
-    delete_files(MojoSetup.downloads)
-    delete_files(flatten_manifest(MojoSetup.manifest, prepend_dest_dir))
-    do_rollbacks()
-    delete_scratchdirs()
-end
-
-
 local function calc_percent(current, total)
     if total == 0 then
         return 0
@@ -605,7 +609,7 @@
         if #wildcards > 1 then
             single_match = false
         else
-            for k,v in ipairs(wildcards) do
+            for i,v in ipairs(wildcards) do
                 if string.find(v, "[*?]") ~= nil then
                     single_match = false
                     break  -- no reason to keep iterating...
@@ -632,7 +636,7 @@
             if wildcards == nil then
                 should_install = true
             else
-                for k,v in ipairs(wildcards) do
+                for i,v in ipairs(wildcards) do
                     if MojoSetup.wildcardmatch(ent.filename, v) then
                         should_install = true
                         break  -- no reason to keep iterating...
@@ -927,10 +931,10 @@
     -- Man, I hate escaping shell strings...
     local bin = "\"`dirname $0`\"'/" .. MojoSetup.metadatadirname .. "/" ..
                 MojoSetup.controlappname .. "'"
-    string.gsub(bin, "'", "'\\''")  -- Escape single quotes for shell.
+    string.gsub(bin, "'", "'\\''")  -- !!! FIXME: no-op!-- Escape single quotes for shell.
 
     local id = MojoSetup.install.id
-    string.gsub(id, "'", "'\\''")
+    string.gsub(id, "'", "'\\''")  -- !!! FIXME: no-op!
 
     local script =
         "#!/bin/sh\n" ..
@@ -965,12 +969,14 @@
     local package =
     {
         id = MojoSetup.install.id,
+        vendor = MojoSetup.install.vendor,
         description = MojoSetup.install.description,
         root = MojoSetup.destination,
         update_url = MojoSetup.install.updateurl,
         version = MojoSetup.install.version,
         manifest = MojoSetup.manifest,
-        splash = MojoSetup.install.splash
+        splash = MojoSetup.install.splash,
+        desktopmenuitems = MojoSetup.install.desktopmenuitems
     }
 
     -- now build these things...
@@ -981,6 +987,90 @@
 end
 
 
+local function freedesktop_menuitem_filename(pkg, idx)  -- only for Unix.
+    local vendor = string.gsub(pkg.vendor, "%..*$", "", 1)  -- chop off TLD.
+    local fname = vendor .. "-" .. pkg.id .. idx .. ".desktop"
+    return MojoSetup.metadatadir .. "/" .. fname
+end
+
+
+local function uninstall_desktop_menu_items(pkg)
+    -- !!! FIXME: GUI progress?
+    if pkg.desktopmenuitems ~= nil then
+        for i,v in ipairs(pkg.desktopmenuitems) do
+            if MojoSetup.info.platform == "windows" then
+                MojoSetup.fatal(_("Unimplemented"))  -- !!! FIXME: write me.
+            elseif MojoSetup.info.platform == "macosx" then
+                MojoSetup.fatal(_("Unimplemented"))  -- !!! FIXME: write me.
+            elseif MojoSetup.info.platform == "beos" then
+                MojoSetup.fatal(_("Unimplemented"))  -- !!! FIXME: write me.
+            else  -- freedesktop, we hope.
+                local fname = freedesktop_menuitem_filename(pkg, idx)
+                if not MojoSetup.platform.uninstalldesktopmenuitem(fname) then
+                    MojoSetup.fatal(_("Failed to uninstall desktop menu item"))
+                end
+            end
+        end
+    end
+end
+
+
+local function install_freedesktop_menuitem(pkg, idx, item)  -- only for Unix.
+    local icon
+    if item.builtin_icon then
+        icon = item.icon
+    else
+        icon = MojoSetup.destination .. "/" .. item.icon
+    end
+
+    local str = "[Desktop Entry]\n" ..
+                "Encoding=UTF-8\n" ..
+                "Value=1.0\n" ..
+                "Type=Application\n" ..
+                "Name=" .. item.name .. "\n" ..
+                "GenericName=" .. item.genericname .. "\n" ..
+                "Comment=" .. item.comment .. "\n" ..
+                "Icon=" .. icon .. "\n" ..
+                "Exec=" .. item.commandline .. "\n" ..
+                "Categories=" .. flatten_list(item.categories) .. "\n"
+
+    if item.mimetype ~= nil then
+        str = str .. "MimeType=" .. flatten_list(item.mimetype) .. "\n"
+    end
+
+    str = str .. "\n"
+
+    local dest = freedesktop_menuitem_filename(pkg, idx)
+    local perms = "0644"  -- !!! FIXME
+    local key = MojoSetup.metadatakey
+    local desc = MojoSetup.metadatadesc
+    install_file_from_string(dest, str, perms, desc, key)
+    if not MojoSetup.platform.installdesktopmenuitem(dest) then
+        MojoSetup.fatal(_("Failed to install desktop menu item"))
+    end
+end
+
+
+local function install_desktop_menu_items(pkg)
+    -- !!! FIXME: GUI progress?
+    if pkg.desktopmenuitems ~= nil then
+        for i,item in ipairs(pkg.desktopmenuitems) do
+            if not item.disabled then
+                if MojoSetup.info.platform == "windows" then
+                    MojoSetup.fatal(_("Unimplemented"))  -- !!! FIXME: write me.
+                elseif MojoSetup.info.platform == "macosx" then
+                    MojoSetup.fatal(_("Unimplemented"))  -- !!! FIXME: write me.
+                elseif MojoSetup.info.platform == "beos" then
+                    MojoSetup.fatal(_("Unimplemented"))  -- !!! FIXME: write me.
+                else  -- freedesktop, we hope.
+                    install_freedesktop_menuitem(pkg, i, item)
+                end
+            end
+        end
+    end
+end
+
+
 local function start_gui(desc, splashfname)
     if splashfname ~= nil then
         splashfname = 'meta/' .. splashfname
@@ -1022,6 +1112,12 @@
         MojoSetup.fatal(_("BUG: support_uninstall requires write_manifest"))
     end
 
+    -- Desktop icons should probably require uninstall so we don't clutter
+    --  the system with no option for reversal later.
+    if (install.desktopmenuitems ~= nil) and (not install.support_uninstall) then
+        MojoSetup.fatal(_("BUG: Setup.DesktopMenuItem requires support_uninstall"))
+    end
+
     -- Manifest support requires the Lua parser.
     if (install.write_manifest) and (not MojoSetup.info.luaparser) then
         MojoSetup.fatal(_("BUG: write_manifest requires Lua parser support"))
@@ -1408,7 +1504,7 @@
             end
         end
 
-        run_config_defined_hook(install.postinstall, install)
+        install_desktop_menu_items(install)
 
         if install.support_uninstall then
             if MojoSetup.info.platform == "windows" then
@@ -1418,6 +1514,8 @@
             end
         end
 
+        run_config_defined_hook(install.postinstall, install)
+
         if install.write_manifest then
             install_control_app(MojoSetup.metadatadesc, MojoSetup.metadatakey)
             install_manifests(MojoSetup.metadatadesc, MojoSetup.metadatakey)
@@ -1500,6 +1598,22 @@
 end
 
 
+local function real_revertinstall()
+    if MojoSetup.gui_started then
+        MojoSetup.gui.final(_("Incomplete installation. We will revert any changes we made."))
+    end
+
+    MojoSetup.loginfo("Cleaning up half-finished installation...")
+
+    -- !!! FIXME: callbacks here.
+    uninstall_desktop_menu_items(MojoSetup.install)
+    delete_files(MojoSetup.downloads)
+    delete_files(flatten_manifest(MojoSetup.manifest, prepend_dest_dir))
+    do_rollbacks()
+    delete_scratchdirs()
+end
+
+
 local function installer()
     MojoSetup.loginfo("Installer starting")
 
@@ -1650,7 +1764,9 @@
     if uninstall_permitted then
         start_gui(package.description, package.splash)
 
-        -- Upvalued so we don't look this up each time...
+        uninstall_desktop_menu_items(package)
+
+        -- Upvalued in callback so we don't look this up each time...
         local ptype = _("Uninstalling")
         local callback = function(fname, current, total)
             fname = make_relative(fname, MojoSetup.destination)




More information about the mojosetup-commits mailing list