4 Hugues Ross Writes a Devlog
Hugues Ross

7/16/17

Getting Organized - 6 - Tick Tock

Hello again, dear readers. Once again, it's time to make my life a little more productive. In my previous post, I grappled with my computer a little bit in order to bring my to-do list up every morning. This month, I'm going to work on my time management a little.

When I mentioned that I wanted a timer back in May, I said that I wanted "I'd like something that fits with everything else". I'm not entirely sure how many purely hypothetical beers I drank before writing that thought, but looking back it sounds more like the mutterings of the local town loony than anything important. I assure you, however, that this strange little collection of words will make sense if you read the following two paragraphs. So, y'know, you can just skip them if you're up for a challenge.

So, if you've been reading these posts religiously then you'll know that I use Awesome to manage my desktop. This gives me lots of control and customization options, but also makes using "traditional" desktop apps cumbersome at times. In this case, a quick Google search will find you endless pages of linux timer apps, coming in exactly two flavors:
  1. Pretty (useless) dialog windows that take up half your screen with fancy Large Text
  2. Icons in your system tray
Option 1 is unappealing in general, but also breaks with my window-management preferences. I could let one of these apps ignore Awesome's tiling rules, but then it would also block me from interacting with whatever's underneath it. Oh, and it's definitely not getting its own special desktop, so that's out. Option 2 would work nicely...if I had a system tray. I could add one and watch it fill with useless incongruous icons, or I could not do that and sleep well at night. But, there's another solution. Let's take a look at my bar for a moment:
Pretty much all of those nice little UI bits are custom. So, I can make my own "Option 2" that fits with the visual style of my desktop and does exactly what I want.

If you're still following along with my little scheme to make a timer but you're not sure it's quick or feasible, then I have some good news: It's done. I secretly went and did it this morning. Normally, I write and code together, but I just wasn't in the mood for 6 AM blogging. What was in the mood for was Runescape (???), and I didn't want to miss any deadlines in the process. I suppose you could say I was motivated by laziness, but I'm not about to complain!

The Implementation

The result of my mad morning keyboard mashing was just under 100 lines of Lua code. Most of that is UI boilerplate, but there's about 40 lines of actual logic in there that might interest someone, so let's give that a look:
   52 local timer = gears.timer {
   53     timeout   = 1,
   54     autostart = false,
   55     callback  = function()
   56         seconds_left = seconds_left - 1
   57         if seconds_left == 0 then
   58             awful.spawn({"mplayer", "/home/df458/assets/se/Notify01.wav"});
   59 
   60             local message = string.format("Your timer of %02d:%02d has ended", math.floor(seconds_total/3600), math.floor(seconds_total%3600/60))
   61             if seconds_total < 3600 then
   62                 message = string.format("Your timer of %02d:%02d has ended", math.floor(seconds_total/60), seconds_total%60)
   63             end
   64             naughty.notify {
   65                 title = "Time's up!",
   66                 text = message,
   67                 timeout = 300
   68             }
   69         elseif seconds_left < 0 then
   70             widget:set_visible(false)
   71             timer:stop()
   72         end
   73         progress:set_value(1 - (seconds_left / seconds_total))
   74         if seconds_left < 3600 then
   75             label:set_markup(string.format("%02d:%02d", math.floor(seconds_left/60), seconds_left%60))
   76         else
   77             label:set_markup(string.format("%02d:%02d", math.floor(seconds_left/3600), math.floor(seconds_left%3600/60)))
   78         end
   79     end
   80 }
   81 
   82 function activate_timer(seconds)
   83     seconds_left = seconds
   84     seconds_total = seconds
   85     widget:set_visible(true)
   86     progress:set_value(0)
   87     if seconds_left < 3600 then
   88         label:set_markup(string.format("%02d:%02d", math.floor(seconds_left/60), seconds_left%60))
   89     else
   90         label:set_markup(string.format("%02d:%02d", math.floor(seconds_left/3600), math.floor(seconds_left%3600/60)))
   91     end
   92     timer:start()
   93 end
As you can see, this little code block could probably use some cleaning. With that said, it works pretty well and demonstrates just how easy it is to make a timer. All I really do is set two numbers to how many seconds I want to wait, then subtract one from the 1st number every second. The second number isn't even necessary at all, but I use it to add a progress indicator. Beyond that, the rest is just making a nice text representation that switches between hour:minute to minute:second as necessary. When the timer's running, my little info are looks like this:
It's simple, sure, but it works quite well! Next time, I'll be wrapping up this series with some final touches to task management.

7/2/17

Roundup, June 2017 - Lost in another dimension

It's that time of the month again, and I've made great progress!

Additional Comments

In addition to what was mentioned in the video, I also went back and re-added joystick emulation to the input code, as well as adding a few minor features and fixes all around. Besides the lackluster demo, this has been one of the best months for development this year.

Goals

  • Time left: about 1 1/2 months
  • Application Module: Done 
  • Audio Module: Done
  • Core Module: Done
  • Gameplay Module: Done
  • Graphics Module: Done
  • Math Module: Done
  • Resource Module: Done
As I said in the video, and as is apparent from the list above, I'm just about finished with dfgame for the time being. If you've been following along, you've probably noticed that I quietly removed particles from the goal list. This is because particles are a bit of an unknown for me.

"But wait!" I hear you cry. "Didn't DFEngine have particles?" This is correct, but DFEngine's implementation was super basic and not very good. My standards for features, while not amazing, are definitely higher than they used to be. If I were to make particles now, I'd want to use the GPU or at least some form of SIMD to get good performance. I played around with GPU particles last summer, but I ultimately ran into a few issues that halted progress and moved on to other things. A decent particle implementation will take some research and effort, but I don't know how much so I'm putting it off for the sake of polish.

Some Bad News

The main goal of my "End of August" timeline was to use dfgame for an actual game jam entry. Unfortunately, I just found out that for the first time in 15 years, Ludum Dare is being held at the end of July. Why? I have absolutely no idea. I could theoretically still enter, since dfgame's release is now feature-complete. However, I would be sacrificing about 50% of my polish time to do so.

Instead, I'll just keep an eye on the itch.io game jams page and select one in the right general time period.

As for next month's posts...beats me, I forgot to plan anything. Whoops!

6/26/17

Happy birthday to Me!

First off, let me apologize quickly for missing my post deadline last weekend. I decided to take a second break this month in order to celebrate my birthday without the stress of writing another post. As much as I love working on this blog, it takes enough time and effort for me to want breaks here and there!

Anyhow, you can expect this month's roundup to land on Sunday at the usual time. I've made great progress, so I think it'll be a pretty good update.

6/11/17

Getting Organized - 5 - Some Awesome Work

When we last left off on my quest for good time management, I decided to share my pizza recipe instead of being productive. Clearly, I'm making good progress. Now then, it's time to do some actual programming.

Awesome!

No, that's not your cries of joy. Awesome is the Window Manager that I use on all of my machines nowadays. In addition to laying out my windows in a pleasing fashion, it also exposes a powerful lua API for customizing and scripting, well, everything. This includes the UI, allowing for some very in-depth customizations!

Last time, I made a list of tasks to complete:
  1. Automatically opening my wiki every morning
  2. A simple timer that fits with my workflow
  3. A way to create tasks remotely while at work
Today, I'm going to finish off the first task. Furthermore, Awesome is going to help me with that.

Hello, Wiki!

Originally, I said that opening my wiki in the morning would be "super-easy to do". I lied! I lied to everyone, even myself! And what a lie it was.

An Unexpected Snag

The easiest way for me to make programs start on launch is to add them to my xinitrc file. In this case, I actually can't do that. Because the terminal that I run vim in won't have its settings ready before my xinitrc finishes running. That means that if I use my xinitrc to bring up my wiki, the colors and fonts will be wrong and it'll have a horrifying scrollbar that looks like it crawled out of Windows 3.1.

Obviously, I don't want that. Awesome gives me the ability to run applications, so I can have it launch my wiki. Perfect, right? Wrong! To help debug scripts and configuration, Awesome provides a mechanism to restart it without closing anything. This is also great! However, this means that every time I restart Awesome it'll also open the wiki again. So, we have a conundrum.

The Solution

The second problem can be solved, but it'll take a few small additions. The goal here is to have Awesome run certain programs on startup, but only if it isn't restarting. To do that, I need to store some information across runs.

Conveniently, Awesome has an event that runs when it exits. Even better, that event tells you whether the exit is part of a restart or not! So, I simply write a value to a file based on whether Awesome is restarting or not. Then, Awesome can read the value when it starts and use it to make decisions. The result looks like this:
awesome.connect_signal("exit", function(restart)
    local lastrun = io.open("~/.config/awesome/lastrun", "w")
    if lastrun ~= nil then
        if restart then
            lastrun:write(0)
        else
            lastrun:write(1)
        end
        lastrun:close()
    end
end)

...

local lastrun = io.open("~/.config/awesome/lastrun", "r")
if lastrun ~= nil then
    local was_quit = lastrun:read("*n")
    if was_quit == 1 then
        for i,e in pairs(autostart_once) do
            awful.spawn(e)
        end
    end
    lastrun:close()
else
    for i,e in pairs(autostart_once) do
        awful.spawn(e)
    end
end

for i,e in pairs(autostart_each) do
    awful.spawn(e)
end

As you can see, it's actually pretty simple. Anything that I add to the autostart_once list will only start if Awesome shut down properly last time it ran. On the other hand, anything in the autostart_each list will run every time Awesome runs. This makes for a convenient solution that I can use as much as I want going forward.

So, that was a semi-complicated solution to a relatively simple problem. Come back next time for some tougher-but-more-straightforward scripting.

6/4/17

Roundup, May 2017 - Oh man I sure do hope the mosquitoes don't--

It took great courage and perseverance, but I finally drove back the mosquitoes that definitely ate all of last week's update. So, here it is:


Additional Comments

I had some difficulty capturing/rendering this video. Last time, I used glc for the video capture. However, glc's audio recorder is really buggy and doesn't work on my machine. It's also quite old, so I'm worried about relying on it too much. This time I made a recording with FFmpeg, but it's just a normal desktop capture so it's a bit slower than I'd like. Next time, I'll probably try to capture the video and audio separately. It might be annoying to line the two up in editing, but it will probably result in a higher-quality video.

The big gain from rewriting the audio system is that multiple audio players should now be able to play audio from the same source at the same time. This means that I don't have to reload a sound effect every time I want to use it, and instead I can easily load once and play hundreds of times. As you can guess, this is really useful for games where you could easily have a couple dozen objects that can make the same sound.

Goals

  • Time left: about 2 1/2 months
  • Application Module: Done 
  • Audio Module: 95%
  • Core Module: Done
  • Editor Module: Not Started
  • Gameplay Module: 80%
    • Missing joystick emulation for input
  • Graphics Module: 65%
    • Missing sprites
    • Missing text
    • Missing framebuffers
    • Missing particles
  • Math Module: Done
  • Resource Module: 65%
    • Missing sprite loading
    • Missing font loading
This month went a little slower than I'd hoped, and some of the code that I wrote feels a bit rushed. As a result, I'm going to need to do another quick pass on the audio and resource modules at some point, just to tighten them up a bit. That's not what I'm doing for June, however. I want to get the last couple essential features, sprites and text, done. This will happen as part of a greater attempt to finish off the graphics module as a whole. I don't expect to finish all of it this month, but it wouldn't surprise me too much if I could sneak framebuffer support in.

If all goes to plan as it has so far, I'll have over a month left over to clean up any loose ends and improve my workflow in preparation.

Next month's posts will feature lots of Linux shell scripting. Much like in May, I'll be kicking off next week with some more organization work.