Guide to making a Shell program (GM OS)

O

orSQUADstra

Guest


Hi, and welcome to the guide of making your own Shell program for GM OS. Please note that this thread will get edited regularly to be up-to-date with the newest functions and/or changes of the OS.

The GM OS Project

Get to know more about GM OS by clicking on the image below:

The basics

First of all, what are Shell programs?


Shell programs are folders with the ending of ".shl". These folders are recognized as a program by GM OS.

A Shell folder should contain two files: A text file named "shellcode.txt", which contains code written in GML, and an image named "shellicon.txt". The names should be exactly these, otherwise the OS won't read them. Having the shellicon.png is optional.


Preset fonts

Here are the fonts that are set in the project file. If you feel like you need to, you can set up your own fonts, or use a sprite font.


The window system


Before you would start making your Shell program, it is important to know the essential things about windows in GM OS.


The graphic above shows how the display works.

Here are the default variables set up by the shell_window_create(...) script:
w - the width of the window
h - the height of the window
active - either 1 or 0 - shows whether the window is active or not. It is active if the last click was inside the window. It is set automatically. You should not change it manually.
ownsequence - Part of the window ordering. Automatic. Don't change it manually.
title - The title of the window, also displayed on hover on the Dock.
resizable - 1 if the window should be resizable, or 0 if it should not.
drag - Returns 1 if the window is being dragged.
mouse_blocked - Returns 0 if the mouse isn't blocked by another window, context menu, dock, etc.
path - this is the path of your folder ending with ".shl". If you need to load sprites, you can put them in your .shl folder, and then use this variable to add the sprites.
icon - the icon of the program, "shellicon.png", or if it's not persistent, then the shell sprite that is built into the project.
dockpos - returns the position on the dock. (0 - first icon on the dock, 1 - second icon on the dock, ...)
action - handles the close, maximize, minimize buttons. Automatic, but you can change it to 1 to close the prorgam, 2 to maximize, 3 to minimize. Note: if you want to force the window to close, always use action = 1, and not instance_destroy() or anything else.
resizew - horizontal resizing. -1 if by the left side, 1 if by the right side. 0 = not being resized right now. Automatic, should not be changed manually.
resizeh - vertical resizing. -1 if by the top, 1 if by the bottom, 0 if not being resized. Automatic, don't change it manually.
Some global variables:
global.theme - 0 = dark theme, 1 = light theme.
global.syscolor - the color scheme of the OS.
global.background - the background sprite.
global.titlebarheight - the height in pixels of the windows. Default is 26.
global.dockn - the number of items displayed on the Dock
global.doubleclickDelay - The delay in frames between one click and another to be counted as a doubleclick. Default is 45.
global.doubleclick - returns 1 for one frame if the user doubleclicked. Note: if the mouse is moved between two clicks, the doubleclick is automatically cancelled.
global.drag - returns 1 if the left mouse button is being held down
global.draghspeed - returns the horizontal speed of the mouse.
global.dragvspeed - returns the vertical speed of the mouse.
Required scripts:
  • shell_window_create(width (real), height (real), title (string), resizable (real)) - takes care of default variables. Should be used in Create event.
  • window_step() - takes care of everything. Should be put in the Step event.
  • draw_window() - takes care of drawing the base window and resizing (if enabled). Use in Draw event, preferably right in the first line.
The windows doesn't use any sort of views, just simply draw themselves. Even though, it is highly recommended to use surfaces if you're doing something more complex. Also, what is very important, always make sure to do a surface check, and if you don't need the surface, use a surface_free(...).

Other useful scripts:
  • point_in_region(x to check, y to check, x1, y1, x2, y2) - returns true if the specified (x, y) is in the (x1, y1, x2, y2) region, otherwise false.
  • mouse_in_region(x1, y1, x2, y2) - returns true if the mouse coordinates are in the (x1, y1, x2, y2) region, otherwise false.
  • get_button(x, y, w, h, str, blocked) - Use in Draw event - returns true if the mouse left button is released within the x, y, x+w, y+h area. in the "blocked" part, always put "mouse_blocked".
Context menus

You might want to use context menus in your program, and of course, this is possible.



Here's what you need to know about GM OS' context menus:
  • There is always only one persistent.
  • If you click anywhere outside, it'll be destroyed and the variable selected will be -1
  • When clicking inside, it'll return the number of the text in the selected variable. In the example above, clicking "Open" would return 0, while clicking "Properties" would return 6.
  • Context menus offer you 10 string and 10 real slots to store data in. You can refer to them as storedString0 ... storedString9 and storedReal0 ... storedReal9. By default, stored reals hold 0, while strings "".
  • The maximum number of listed items is 100.
  • Context menus store a variable called linkedTo, which you should always set to the id of your window.
Working with context menus

First of all, you need to create the instance objContextmenu.
Then, as the next step, set up your menues, stored variables, and most importantly the linkedTo variable.
The listed items use a 2D array, menu[n,0]. The first number is the number of the string.
Here's the usual way to do it is this:


Code:
if mouse_check_button_pressed(mb_right)
and mouse_blocked = 0
and mouse_in_region(x,y,x+w,y+h)
{
   instance_create(mouse_x,mouse_y,objContextmenu)
   with objContextmenu
   {
      linkedTo = other.id
      storedReal0 = 255
      storedString0 = "Just a string"
      menu[0,0] = "Option 1"
      menu[1,0] = "Option 2"
      menu[2,0] = "Option 3"
   }
}
Then, let's check for what the user clicks..
Code:
if instance_exists(objContextmenu)
{
   with objContextmenu
   {
      if linkedTo = other.id
         {
            if selected = 0 {/*Code for Option 1 here*/}
            if selected = 1 {/*Code for Option 2 here*/}
            if selected = 2 {/*Code for Option 3 here*/}
         }
   }
}


Working with text boxes

Text boxes are structured similarly to context menus.


To create a text box, use the following code:
Code:
inst = instance_create(x, y, objTextbox)
with inst
{
   w = 256
   h = 24
   str = "Hello" //you can skip this line entirely if you don't want the text box to contain any string on creation
   linkedTo = other.id //This is very important, do not forget this!
   destroyOnOuterClick = 0 //if it is set to 1, the textbox will be destroyed when you click outside of it.
}
To get the text from the textbox:
Code:
stringVariable = ""
with objTextbox
{
   if linkedTo = other.id
   {
      other.stringVariable = str
   }
}
Things to know about text boxes:
  • Text boxes store their string in the str variable, see above how to check it.
  • The textbox will move with the window.
  • The textbox sets its own depth and acts like if it was within the window code.
  • Upon destroying your program's window, the textbox will destroy itself automatically.
  • Text boxes are slightly advanced, as you can a set custom pointer position with mouse, or with the left and right keys.
  • The textbox will only be written to when it is active. It's active if the last clicked mouse position is on it, and wasn't blocked by another instance.
This thread is not finished yet. By the time of the first public release of the GM OS, it should be finished and get you ready to make your own Shell programs.
 
Last edited:
O

orSQUADstra

Guest
This looks pretty cool! But why is the top window bar not included in the height?

Because it's only important for the drag, the buttons, and the maximizing. Things that the window_step() script does for you. It's just easier if, let's say you want to draw an image at the top left corner of the window under the titlebar, it's simply better to just write draw_sprite(spr,img,x,y) than draw_sprite(spr,img,x,y+global.titlebarheight) (not to mention you don't have to put that +global.titlebarheight thing literally all over your code :D )
 
O

orSQUADstra

Guest
EDIT: Also, point_in_region( ) mirrors point_in_rectangle() ?
The project uses GameMaker 8 to make the usage of these programs work by the functions object_add(), object_event_add(), etc., which are obsolete and not included in GMS 1.4 or GMS 2. In GM 8, point_in_rectangle wasn't a function yet, so it pretty much mirrors that function, yes.
 
O

orSQUADstra

Guest
What are you using object_add for?
For the Shell programs :p

The way I made 3rd party programs to be possible, if I want to simplify it as much as I can is this:
The program loads in the .txt and runs it which includes the object_add and object_event_add functions, making an object that will then be added to the room with instance_create().
 
O

orSQUADstra

Guest
What are you using object_add for?
Here's an example. This is a Shell program that I made as a test, what it does is only displaying a text of "RAINBOW THEME MOD", and a button, what if you press, the global.syscolor will be constantly changed with make_color_hsv, making a rainbow effect:
Code:
12
objname = "obj"+string(current_time)+"test"

variable_local_set(objname,object_add())

object_set_parent(variable_local_get(objname),objWindow)

object_event_add(variable_local_get(objname),ev_destroy,0,'object_delete(object_index)')
object_event_add(variable_local_get(objname),ev_create,0,'shell_window_create(240,120,"",0);check=false')
object_event_add(variable_local_get(objname),ev_step,0,'window_step();if check=true{global.syscolor = make_color_hsv(current_time*0.025,220,220);}')
object_event_add(variable_local_get(objname),ev_draw,0,'action = draw_window(x,y,w,h,title,resizable);draw_set_font(fontSlimSmall);draw_set_color(c_gray);draw_set_halign(fa_center);draw_set_valign(fa_top);draw_text(x+w*.5,y+16,"RAINBOW THEME MOD");if check = false{if get_button(x+w*.5-50,y+h*.5,100,20,"A Button",mouse_blocked){check = !check}}else{if get_button(x+w*.5-50,y+h*.5,100,20,"DEACTIVATE",mouse_blocked){check = !check}}')
instance_create(100,100,variable_local_get(objname))
Note: The first row is always just a number, which should be exactly the number of lines of your .txt file, with the first line included.
 
O

orSQUADstra

Guest
Does object_add allow you to import and run uncompiled code in a compiled build?
Pretty much. It's just what it says. It adds an object, and with object_event_add, you add events to it. Like if it was in the project file, but of course it doesn't get compiled (afaik, at least).
 
O

orSQUADstra

Guest
What is the purpose of this project? If you don't mind me asking :p
Like, what scenarios will it be used in?
It's just a project purely made for fun.
I think it's just interesting to see something like this being made with GameMaker. Of course many people made fake operating system with gamemaker, but none of them are even close to good (at least the ones I've seen). So, having one that is actually functional, actual windows, window ordering, custom background, actaully functioning file browser, and so on. Just stretchin' GM8 to the limits :D
 
O

orSQUADstra

Guest
I'll be updating this post further more soon! But until then, I have a question:
Adding sprites and audio files to your project will be explained in this guide, but do you guys think you'll need a specific tutorial video to understand this whole thing better?

If so, I'd probably recreate something similar to this Shell program I just made:



Probably not 1:1 this, as it has to load in 12 wav files and 4 sprites, but something a bit simplier, just for the sake of simplicity and to make things as short as possible.
 
Last edited:

trg601

Member
I think it would be helpful, but I'm not sure it is necessary.
Also, when are you going to release this to us? I already made two shell programs XD (I can't be sure if they will work yet though).
 
O

orSQUADstra

Guest
I think it would be helpful, but I'm not sure it is necessary.
Also, when are you going to release this to us? I already made two shell programs XD (I can't be sure if they will work yet though).
The OS? Still a few days. I added a few more things to the plan, and I don't want to publish it with heavily unfinished things. :D But I'm going to hit you up with a conv in a moment
 
O

orSQUADstra

Guest
This looks really awesome. I suggest you consider releasing it under an open source license!

That way, the community can contribute code, and your system will be adopted more widely.

Moreover, I'd gladly support this project on Patreon! I'm sure others would as well.

Consider using the GPLv2 or LGPL. This way, commercial groups can use your codebase and still contribute to the community overall.


I tend to sound like an ideologue about these things sometimes... I'll try not to persuade you against your best interests. I gave some facts, I ask that you consider them. ;)

EDIT: Wish we could still do execute_string() :(

EDIT 2: I'll contribute if you let me.
Thanks, I'll think about it! :)
 
O

orSQUADstra

Guest
Hi everyone!
Please note that the Shell programs will work a bit differently from now on. Once it's actually finished, I'll update this thread to represent the current needs. Some of the major changes are already done, so instead of a messy code it'll be much simpler.


I will also do video tutorials, all function and script will have it's own dedicated short video.

Hope you guys like the way this project, and the writing of Shell programs goes :D
 
Last edited:
O

orSQUADstra

Guest
This is Game Maker related. Why is it in the Off-Topic?
When I created the topic I just wans't sure where I should post it, and I assumed that only people who are interested in the project will even look at this. But I might ask someone to move it to Tutorials (even though it might not fit 100% in there)
 
O

orSQUADstra

Guest
How do u run the shell? with gml_pragma?
No, I just make it load in the text files and save them as strings, then with execute_string I make a new object and make them the create, step and draw event of the shells (+ the default window code on top of that)
 

Evanski

Raccoon Lord
Forum Staff
Moderator
So theoretically speaking a person could write malware for GM-OS,
Will GM-OS come with anti-virus?
 
O

orSQUADstra

Guest
So theoretically speaking a person could write malware for GM-OS,
Will GM-OS come with anti-virus?
The "runner" runs through the code and checks for some suspicious stuff (like if it would delete a system object or if it would try to access files outside of the main directory of GM OS). Obviously it's not near perfect, but even that is better than nothing. If it does detect something then it'll give a warning, and you have to confirm whether you really want to run it or not. But in all honesty, I don't expect anyone to make an actual virus for it or anything :D
 

Evanski

Raccoon Lord
Forum Staff
Moderator
Tempted but Idk yet
The "runner" runs through the code and checks for some suspicious stuff (like if it would delete a system object or if it would try to access files outside of the main directory of GM OS). Obviously it's not near perfect, but even that is better than nothing. If it does detect something then it'll give a warning, and you have to confirm whether you really want to run it or not. But in all honesty, I don't expect anyone to make an actual virus for it or anything :D
A text file named "shellcode.txt", which contains code written in GML
So looking back on this, if the Shellcode.txt allows all GML code you could make a simple Trojan virus, That could use how GML does code to its advantage like, changing how text is drawn, messing with surfaces, changing values of variables and such. then again you could just have one that just runs game_end(); but all of that can be easily blocked, idk how exactly your project can read code from .txt (Would love to know how btw) but using the same system you could put in a filter that finds and either "removes" or tells OS to not run the file, (like windows popping up saying "This file cannot be run as its corrupted or contains harmful scripts"), all in all im just fascinated by this project! :)
 
O

orSQUADstra

Guest
So looking back on this, if the Shellcode.txt allows all GML code you could make a simple Trojan virus, That could use how GML does code to its advantage like, changing how text is drawn, messing with surfaces, changing values of variables and such. then again you could just have one that just runs game_end(); but all of that can be easily blocked, idk how exactly your project can read code from .txt (Would love to know how btw) but using the same system you could put in a filter that finds and either "removes" or tells OS to not run the file, (like windows popping up saying "This file cannot be run as its corrupted or contains harmful scripts"), all in all im just fascinated by this project! :)
Thanks! Originally I wanted to implement a filter and a warning popup if anything suspicious is found, however I didn't work much on that part since.
Now, I've been thinking about moving the project to GMS 2 (obvious advantages like disabling the alpha channel that would fix the surface problem that is currently present, as well as shaders to replace the blur script, and also it would run much better to begin with). There will be a sandbox on/off toggle in GMS2 sometime in the future if I remember right so that's a good thing, but I'll have to redo how shells will work as my current method relies on object_add() and object_event_add() functions that are not present in GMS2.
I have been thinking of making up a language that is very close to GML but is structured in a way that I could easily manage it with GM OS.
 
Top