Monday, August 24, 2020

Trials and Tribulations of Getting Gateware to Compile in C++/WinRT

 Since last week, I have finished my first draft of my research paper on the port of Gateware to UWP. In the process of doing that I managed to bring all of the Gateware libraries to an C++/WinRT template project and get it compile successfully. I am not using any of the libraries but it was not a plug and play process, there were compiler errors that needed to be taken care of. This is blog post I will be going over what I had to do to get it to compile.

The first thing I did was include the libraries in a C++/WinRT template:


pch.h:
Top of the "main" file:


The First Error

This led me to GRasterSurface_win32.hpp.

This function is found in wingdi.h. Turns out it is not found because it is found under:

This led me down the rabbit hole of "What is WINAPI_PARTITION_DESKTOP?" After some googling I found a Microsoft doc that went over what it is:
So SetDIBitsToDevice only works on a Win32 desktop app as a UWP app is WINAPI_PARTITION_APP.
I ended up fixing this by just bypassing GRasterSurface_win32 and using the dummy implementation for now.
That fixed the compiler errors.

Prototyping a GWindow Implementation

Using what I found out with the WINAPI definitions, I edited GWindow.hpp:
GWindow_UWP.hpp is the prototype. I started to fill out some of the cookie cutter stuff in my implementation:

Summary

This exercise was immensely helpful in giving me ideas on how I will be doing my port over the next 3 months. It also helped quite while finishing up my rough draft over the last week. I do see the example "triangle.h" that we give end-users changing quite a bit. I also believe end-users are going to have to know some C++/WinRT API calls, this will have to be disclosed to them.

The next 3 months are going to be busy and I definitely have my work cut out for me. 

Resources

Saturday, August 22, 2020

The Mouse Move Event

GBufferedInput is an event-based library that provides raw input. The goal of the mouse move feature is to give the developers a message every time the mouse changes position. One of the conditions of this event is to report pixel changes in mouse movement only. This condition exists because there are platforms that report this event regardless of any change in position. 

Feature implementation steps
The first step in working on this feature is to add the new 
GBufferedInput event flag, MOUSEMOVE. Next is to understand how GBufferedInput creates events from the information it receives from each platform. Finally, add the event to the GBufferedInput implementation in a way that conforms with the established behavior of the existing code.


Windows

On Windows, GBufferedInput replaces the procedure Windows sends events to, with one that is set up by GWindowGBufferedInput reports events to GWindow after it is done with them. Utilizing the raw input model, GBufferedInput gets input data directly from the device and uses it to generate events. 


Multiple mouse movement events are provided in the raw input model.


  • Relative Movement: Provides the change in the mouse position.
  • Absolute Movement: Provides the screen position of the mouse.
  • Virtual Movement: Provides the mouse position within a virtual environment. 

Virtual movement will be ignored since it is outside the scope of the Gateware feature. GBufferedInput will look for relative and absolute movement events. The existing Windows implementation of GBufferedInput only generates one event per message from Windows. The problem is that the raw input model can report a mouse button event with a mouse movement event within the same message. Considering how plentiful mouse movement messages are, I chose to ignore the mouse move event when there is also a button event. I did this initially to preserve the behavior of the existing implementation; one message generates one event. I later changed this implementation to create multiple events, to match GBufferedInput's behavior on every other platform.


Linux

On Linux, the implementation of GBufferedInput does not have a function that X11 calls whenever an event message is ready. Instead, a thread continuously queries input changes. Another difference is that multiple events are sent per query, which is unlike the behavior of the Windows implementation. 


Even though there are mouse move events that can be received from X11, my feature code doesn't use them. The current implementation gets the updated mouse position every thread cycle. Instead of waiting for an event, where the mouse may not have actually moved, I chose to check the current mouse position with its last position and generate the event if there was a difference. Given the implementation's query nature, I believe this is the best option over waiting for the mouse move event. It is possible that in the time between queries, a mouse movement event may be missed, checking the mouse position every cycle is more reliable in this case.


Mac

The Mac implementation of GBufferedInput uses inheritance to get events. Overriding an event function of NSResponder is all that needs to be done to start receiving events. This is also unlike Windows and Linux, and a much easier way to set up events. 


Usually, overriding a member function of NSResponder is all you have to do to start receiving events. However, the mouseMoved() function has an extra step, requiring the window to be enabled to accept mouse move events. By default, a window ignores mouse move events due to how frequently they occur.


Unit Testing catches problems

A Unit Test needed to be made to ensure that the MOUSEMOVE event was working correctly. GBufferedInput didn't have a Unit Test for any mouse input, so I made one. In addition to testing the MOUSEMOVE event, all mouse buttons and the scroll wheel would also be tested. The Unit Test revealed that various events were not responding correctly, depending on the platform. It's an issue that will have to be addressed at some point. Otherwise, the feature is complete.


Conclusion

The MOUSEMOVE event is a helpful feature for UI, FPS movement, and many types of games. With each of these features, I see the different ways each platform operates and how to work with them. I also learn more and more about the inner working of Gateware and how it makes complicated things easier for developers. 

Monday, August 17, 2020

Researching C++/WinRT and UWP

     It's now been a week since my last blog post and I would say my understanding of the structure of Gateware has grown immensely. I focused on the structure and flow of GWindow as that is going to be my initial target for the port. Since last week I have put together an outline for my research document that will detail the research I have done. I will transcribe that outline in this blog post and detail anymore research that still needs to be done.

 Purpose of Research

  • To port Gateware to UWP
    • The challenge
      • UWP is commonly written in C#
      • Need access to modern Windows Runtime APIs
      • Gateware is written in C++11
    • The solution
      • Use C++/WinRT

Research on C++/WinRT

  • What is this?
    • "C++/WinRT is an entirely standard modern C++17 language projection for Windows Runtime (WinRT) APIs, implemented as a header-file-based library, and designed to provide you with first-class access to the modern Windows API. With C++/WinRT, you can author and consume Windows Runtime APIs using any standards-compliant C++17 compiler."[1]
  • Why is it a solution?
    • This will allow us to port Gateware to UWP without having to switch programming languages. 
  • What problems could arise?
    • It needs C++17, will this be an issue?
      • To be answered
    • Will this not work with the single header file structure of Gateware?
      • To be answered
    • Will the end user need to know C++/WinRT API calls?
      • Could we abstract this so the user wouldn't know that it is present?
        • To be answered
    • As of right now, my understanding of C++/WinRT is that you must start a project with one of their templates if you wish to use it. Another option that Microsoft suggests is to add the functionality to an already existing project(there are tutorials to do so), but I have yet to get this to work the way I would like.

Research on UWP(Universal Windows Platform)

  • What is this?
    • "Windows 10 introduces the Universal Windows Platform (UWP), which provides a common app platform on every device that runs Windows 10. The UWP core APIs are the same on all Windows devices. If your app only uses the core APIs, it will run on any Windows 10 device no matter whether you are targeting a desktop PC, Xbox, Mixed-reality headset, and so on."[2]
  • Why are we porting to this?
    • Microsoft Store
    • Xbox
  • Will need need to use XAML?
    • Microsoft's XML
    • Used to build GUI in UWP
    • UWP can be built without it and as we are going to be rendering with a graphics API, it does make sense that we would not need it.

Research on Gateware

  • What libraries will need the most work?
    • GWindow seems to be the likely candidate.
    • Because of the modular structure of Gateware, most libraries seem as though they will work regardless of what implementation of GWindow is being used.
      • I believe I can get a window handle in UWP, this will allow things like GInput to work as normal(hopefully).

How I could apply this to the port

  • Create another implementation of GWindow for Windows that the user will select when they download Gateware, similar to how they already do when choosing a graphics API.
    • This implementation will use C++/WinRT to get the window handle
      • winrt::handle struct could be used

To be done

    There are still some unanswered questions and a lot more research to be done. Over the next week, I hope to have some sort of a prototype to see how this stuff will mesh together and answer most of the questions I still have. I believe doing so will be really helpful for writing my research document.

Resources

*bullet points that have the sub-bullet "To be answered" need more research

Lock the Window

We want to give developers the ability to lock the size of a GWindow. When a window is locked, the user will not be able to alter the window's size in any way. Moving around the window and minimizing it is still possible, but maximizing the window and adjusting the size is not. The goal of this feature is to give developers more control over how their users experience their content.

Why lock the window
There are plenty of reasons why a developer might want to lock the size of a window: 

  • Prevent the user from seeing non rendered areas of the screen.
  • Prevent distortion of the graphics due to aspect ratio.
  • Save development time instead of creating a resolution-independent HUD.


Feature goals

Changing the configuration style of a GWindow can already be done by passing a GWindowStyle flag to either the Create() or ReconfigureWindow() functions. Activating the lock feature will be done the same way by using a new flag, WindowedLocked. The naming of this flag conforms to the naming conventions of the other GWindow flags. As the other flags do so succinctly, the new flag is named to describe the state the window will be in when the flag is used. In this case, the GWindow will become windowed (not fullscreen or minimized), and the resizing ability will be locked from the user.


Current flags

Each existing flag has a different effect on a GWindow. To understand how the new flag will differentiate from the other flags, I created a window using each of them and took notes.

  • WindowedBordered: Makes a resizable window with a title bar and border.
  • WindowedBorderless: Makes a non-resizable window without a title bar or border.
  • FullscreenBordered: Fills the screen with a resizable window, including a title bar and border.
  • FullscreenBorderless: Fills the screen with a non-resizable window without a title bar or border.
  • Minimized: Makes a window that starts minimized

Looking at how the existing flags change a window, it is already possible to create a non-resizable window using one of the two borderless flags. The real need for the flag is to have a non-resizable bordered style window. For that reason, I made the WindowedLocked flag do everything to a window that the WindowedBordered flag does, except without making the window resizable of course.


Linux implementation

Implementing this feature is different on each platform, but there are similarities. On Windows, combining certain WinAPI flags will create a bordered window that cannot be resized. The Mac implementation is done very much the same way. On Linux though, instead of setting flags, the window's minimum and maximum sizes are adjusted to match the window's size. This prevents the window from being resized. This way of restricting the window's size is odd compared to the other two platforms. 


The cursor will change to a resize cursor when it reaches the edge of the window, but the window won't resize when the mouse drags the edge. Instead, the entire window moves with the mouse.

Locked

The new feature to lock the window is complete. When you have limited time to make an game, you don't want to spend more than a few minutes figuring this out. With just one flag, developers now have even more control over the games they make. 

Friday, August 14, 2020

Implementing SetIcon()

After researching what it takes to set a window icon dynamically, I moved on to implementing it. Each platform handled setting the icon differently, with some platforms having more capabilities than others. These differences presented a challenge as to how to create standard behavior between the platforms. It also generated the question of how to enable developers to use the SetIcon() function to maximize the use of each platform's capabilities.

Where icons can be changed
Three icon display areas can be set dynamically at runtime: the window, taskbar, and alt-tab display. The Mac doesn't have a window icon, so only the dock and cmd-tab display can be changed. Depending on the distribution, Linux may or may not have an icon on the window, but regardless the two other display icons can be changed.

Platform differences
Each platform-specific method for changing the icon will change all icon display areas with the given icon. Where the platforms differ is how different icon display areas can be set independently. On Windows, two different messages can be sent to change either the window icon or both the taskbar and alt-tab icons. On Linux, the message to set the icon can contain multiple icons. The window manager will pick from the list of icons the best fitting icon for each display area. On Mac, there is no way to set icons independently at runtime.

More functionality
I felt that developers should be able to set multiple sized icons if they wanted to. I made the behavior of the SetIcon() function match across all platforms when it is first called. When the function is subsequently called, the Mac implementation continues to behave the same, while Windows and Linux behavior differs. The implementation of these two platforms attempts to set individual icons based on the size of the icon provided to them.

Mac functionality limited
I attempted to give the Mac implementation the same behavior as the other platforms for consecutive calls. However, I was unable to find a way to do so. To set an icon on the Mac dynamically, you create an NSImage and assign it as the applications icon image. I use the icon pixel array to make the bitmap representation that gets added to the NSImage I create. One of the interesting features of NSImage is the ability to add multiple image representations. There is even a handy function that will grab the best fit of these representations for a given area. I thought adding different sized icons as image representations would have a similar result as the icon list on Linux. However, this was not the case. I also tried naming each icon image and appending the names with @1, @2, @3, etc. This how icons of different sizes are bundled together in Xcode. Dynamically naming the image representations doesn't work, and I have found documentation that suggests icon bundling can only be set up through Xcode and not dynamically at runtime.

Feature notes
There are few things anyone using the SetIcon() function should be aware of. Windows 10 icons are 16x16 and 24x24. Linux can vary depending on the distribution. On my version of Linux Mint, icons are 16x16 and 48x48. If you don't use these sizes, both platforms will scale the icon to fit the displayed area. Scaled icons look blurry, which is why I am providing the dimensions I have found. Setting icons on the Mac is not as forgiving, and the icons do not display correctly when outside of a designated size. Acceptable sizes for the Mac are 16x16, 32x32, 48x48, 128x128, 256x256, 512x512, and 1024x1024. I have tested all of these icon sizes, except for the last two.

Feature complete
A convenient function to dynamically set icons is a fun feature. It is less complicated than figuring out how to bundle the app icon on each platform. The subsequent call feature of the SetIcon() function provides a way to keep those icons crisp and professional looking. Plus, the dynamic nature of the SetIcon() function enables developers to be creative about it. I imagine an icon of the doom guy changing as the player moves around or takes damage. Overall, this is another fun feature I am glad I got to work on.

Monday, August 10, 2020

Welcome to Gateware

I've been in the Gateware community (discord) for a little over a month or so but only became a developer last week. My goal for the project is to successfully port it over to Microsoft's Universal Windows Platform (UWP). From there it could be brought to Xbox, opening up that option for future Full Sail students and Gateware devs to use in their capstone projects. Over the last week, I have gotten access to the codebase and successfully gotten it to compile on both Windows and Linux. There was one hiccup with the Linux build, but Ozzie got me back on track and fortunately it was just a simple fix. Turns out you shouldn't try and run Unit Tests on old builds and expect them to work. I've still got quite a bit to learn about the project as a whole and hope to have more of a fluent understanding of it next time I sit down to write one of these.

Friday, August 7, 2020

Setting Icons

The new feature

GWindow's can detect window events and set a window's name, but it cannot set the window's icon. The goal of the new feature is to enable developers to set the icon. It should also be as simple as calling SetIcon() while passing in an array of pixels.


Research

Windows, Mac, and Linux platforms are all going to need an implementation of this feature. I started by researching how the icon can be set dynamically on each platform. I specifically want to know what commonalities there are between platforms, to inform the paraments SetIcon() will require.


Windows

On Windows, the icons on the window, alt-tab display, and taskbar can be changed at runtime. The window icon is 16x16, while the alt-tab and taskbar icons are 24x24. All of these icons are changed when the first message is sent to Windows to change it. As a result, icons get stretched or squashed when they are not the right size for where they are displayed. Fortunately, two separate messages can be sent for different icon sizes. This means we can set the window's icon separate from the icon for the alt-tab display and taskbar. Enabling developers to keep their icons looking crisp by using multiple premade icons of different sizes. 


Linux

Like Windows, a message can be sent to X11 to change the window, alt-tab display, and taskbar icons. However, there is only one message to set icons, and all icons are set in that single message. Also, like Windows, squashing and stretching occurs depending on where icons are displayed. Unfortunately, I will not be able to test if the window icon changes on my computer because my version of Linux Mint doesn't have them.


Mac

Just as with Linux, the windows on Mac do not have icons. The dock icon can be set, but I haven't been able to find if the alt-tab icon can also be set. Icons can be a range of sizes upwards of 1024x1024. The maximum size appears to change every few years. 


Parameters

Every platform will require, at some point, a size (width and height) and an array of pixels before setting the icon. Therefore, the function is going to end up looking something like SetIcon(unsigned int width, unsigned int height, void* pixels).


In progress

Adding this feature seems simple so far. However, the implementation of it on each platform is going to vary. The challenge will be in keeping the result of calling the SetIcon() function the same across each platform.