Coding period for GSoC 2020 has started and I have begun my work on my summer project. As said in my introductory post, I will be working on adding functionality to create and manage game collections in GNOME Games with help from Alexander (@alexm). After the project is complete, it will provide users with a shiny new ability to add any games to their own custom collections. And some additional feature to provide users with a quickly accessible, automatically generated collections such as recently played, favorites and hidden games.
I started out by separating the work into independently manageable chunks so that I can open several smaller merge requests, rather than a single large one, which I can imagine would be horrible to manage, and even worse for Alexander to review. And my code, however small it is, usually needs a lot of fixing.
So the first chunk I decided to work on is… Selection Mode! I decided selection mode would be the best part to start with so that when I get to modifying the database part to store all the collections and the games in it, I will have all the necessary functionality to test it with actual real world data rather than some made up data using temporary spaghetti code.
Selection mode will help the user select games to be added to their collections. It may also be useful later on for anything that requires a user to choose a list of games (maybe even a list of collections), since its not tightly integrated with collections.
Selection mode in Games is just like in most other apps. There will be a button in the header bar which can be clicked to enable selection mode. It changes the header bar into a blue “selection-mode” styled header bar with options to search, change selection modifier and cancel the selection mode.
On to implementation details, I use
GtkButtons for the selection mode and cancel buttons. A
GtkToggleButton for the search which already works as is. A
GtkMenuButton for the selection modifier with select-all and select-none actions. A simple
is-selection-mode prop which is bound, propagated and listened to, for it all to come together. And when selection mode is enabled the header bar changes, and the
GtkCheckButtons in an overlay inside the game thumbnail reveals itself smoothly. Each game has it’s thumnail in a
GameIconView, and several of them are children to a
GameIconView has a
checked property which when set automatically handles all the stuff of adding/removing games to a
selected_games. Since Vala arrays do not have an inbuilt remove element function, Alexander directed me to
GenericSet which works nicely here.
The select all action can be used to select all games in the current page. Which means that apart from selecting all games in the main games page, it can also select all games that only belong to currently selected platform, or select all games that only match the user’s search query.
Since Games uses Libhandy (A very cool library for adaptive and sleek widgets and UI), its a very adaptive app, and the selection mechanism requires some slight tweaks. At first I had some trouble with all the edge cases when the
HdyLeaflet folds from desktop view to a more compact UI. Thanks to Alexander, I realized I shouldn’t be manually changing a lot of unrelated props in unrelated places, and that made the code much much better with a lot fewer bugs.
And now, I present to you the current state of selection mode:
Some things to note:
- While most of the selection code is ready to go, it still isn’t really complete without a bottom bar which presents the user options to add games to a collection, or mark it as favorite/hidden. This will be done in the up coming weeks with work on the database and
- For those nice blue checkboxes to work without any workarounds, it needs a little Adwaita theme fix. Alexander helped me open my first MR to GTK which will remove the need for any css style workarounds to get that blue checkboxes for use cases such as in this case.
- There is a slight hiccup with selecting multiple games quickly. It happens even when input is “slow enough” for some real world usage, so this might annoy some users who are a bit quick with their inputs. But we couldn’t yet pinpoint what causes it, but it happens with other apps that use
GtkFlowBoxtoo, so probably not something that Games is doing.
The next part to work on as said above would be modifying the database to support collections. It will need new queries to add games to collections, get games in a certail collection, delete, rename etc. And then I will work on a
CollectionPage where users can see Recently Played, Favorites, and then their custom collections and manage it.
I think I’m moving in the right pace, maybe even a bit fast, but thats alright I guess, becuase my exams can come up at any day in the upoming weeks, and no one really knows exactly when, due to recent events.
You can view the selection mode MR here.
Other stuffs I’ve been doing
If you are still here, I can share some other stuff I’ve been poking around with :)
Finally scratched my itch to checkout OpenGL
Some games/emulators produce a rotated display output which makes the game unplayable. So I thought I’d give a shot at trying to implement display rotation for Games. Games uses retro-gtk which is a GTK+ Libretro frontend framework. It helps Games work with libretro games/emulators. So that’s the place where I should poke around with to implement display rotation.
I honestly don’t know what I expected, but I played with the first thing that I suspected and voila, it actually worked, the output was rotated. It turned out that I was playing with texture coordinates and simply “cycled” it and the display was rotated. The End…except no, it isn’t that simple, because that’s a bit of a hack™.
What I did, could technically be used, but it’ll not flexible later on. One particular use case suggested by Alexander was adding animations on rotating, so that its more elegant. So I should look for a more flexible solution. And so began my journey in lands of OpenGL. I never knew a thing about OpenGL, but I was always interested in how graphics/video cards worked. And I have all the time I need, since its a lock down due to COVID-19. And the solution was to use GLSL shaders and framebuffer objects or shortly
So shaders are little programs that run on the graphic cards which is incredibly parallel. So to rotate a texture (think of texture as a game’s output in this case), I need to write a simple vertex shader which rotates every vertices (here 4 since its a rectangle) of the texture, and a basic pass-through fragment shader (which is a copy pasta since I don’t need to modify the pixels in the texture). So I do that. But I cant just use these shaders yet, because retro-gtk use another set of shaders to support video filters such as smooth, CRT effects which can be applied on top of the game’s video output for a retro feel. So I will need to first render the output with video filter effects and then rotate it using my brand new shaders. And for that I need to use an offscreen framebuffer. Think of framebuffer as something that holds information about color of pixels, and how “deep” a pixel is from the screen etc. For this use case, really think of it as a temporary place to store a texture. Because what I did was to draw the texture with video filter onto the offscreen framebuffer, then use our shiny new rotate shader to rotate the output in the offscreen framebuffer and “draw” it onto the default framebuffer which is what you see on the screen. This method of rendering to an offscreen framebuffer for sampling it in the next stage of the pipeline is often called “render to texture”.
I was stuck (blank output) at rendering to the default framebuffer until Alexander helped me understand that at the final stage I should bind to
GtkGLArea’s framebuffer and not the default framebuffer, since retro-gtk uses a
GtkGLArea for rendering with OpenGL. Other issue I was stuck with for a month was that rotating corrupted the window’s headerbar in client side rendering. I was very frustrated with this bug, because it seems to be working but something that is unfixable by me made it unusable. Or so I thought. Again thanks to Alexander, I realized I shouldn’t call OpenGL functions outside functions provided by
GtkGLArea. Finally with it all working here’s a demo:
Hopefully it will be available soon. Regardless, this was an awesome experience. I worked in C, a short introduction to OpenGL, and learned basic dynamic memory management.
You can see this work here.
A Cairo Adventure
This is one of my recent projects. I felt Game’s thumbnail covers could have a bit more eye candy instead of the letter-boxed covers which you can see in my first selection mode picture. So I thought of blurred enlarged background of the cover behind the cover for the 1:1 cover aspect ratio instead of letter-boxing it. Alexander gave me the green light and I began search for how to do it. I assumed someone on the internet already had this done blur with cairo. So I created a simple app to test out different blur implementations. But most of them did not produce the right effect we were looking for and I was too lazy to tweak it for the required effect. And after trying about 4 different implementations I almost gave up. But then Alexander directed me at GTK’s own blur used for drawing shadows. So I tried it out and it kinda works, but only that the colors go away and picture becomes wavy with higher blur radius. That’s because I had commented out an assert that made sure the image I give it is in A8 format (only alpha), so that it doesn’t crash when I give it RGB24 images just for an experiment :p.
But I had an idea. In the hopes that GTK’s cairo blur was initially implemented in RGB24 I checked out the gtkcairoblur.c’s history, and… it was! It supported ARGB32, RGB24, and A8. After some quick ctrl+c ctrl+v which is what I’m good at, It worked! And now I need to port it to Vala so that it can be used in Games (without extern funcs).
You can see my work up until now here. I added flatpak and CI support cause why not :). And finally here’s a sample of what the covers would look like with blurred background:
I’m really having a great time contributing to GNOME. It’s exciting and the community is very nice and helping. And especially thanks to my mentor, Alexander, for all the help.
I’ll try blogging at least once or twice a month :). But until then goodbye!