Spoiler
This is an example of running multiple shell programs in a sequence by calling one 'alias' program. It will show you how to use the 'char' and 'key' events to do so.I can already tell a few of you are like "what in the world..", as you may not understand what this I'm talking about. Don't worry, I only make the title so long because I (strangely enough) cannot think of a clever name for this kind of program.
I haven't seen any programs made quite like this, but it's a simple and interesting concept I've quickly thought up that could help some of you who don't like to use "shell.run(…)" 5 times in a row (or something like that). Think of it as an alias for multiple programs.
This program allows you to run ANY NUMBER of programs in a preset sequence, allowing you to call a (virtually) infinite number of shell programs by using one program. Pretty neat, pretty nifty, pretty useless, if you don't need it. Could be useful for those people who have multiple startup files that they need to rotate through, or those people who have like 3 OS' that they want to use and they need to do a lot of file moving and renaming etc.
Where's the code already?
Well, I'm going to run through it all so you understand what's going on..(don't worry, there's not much code at all).
First bit of code? The table. And I mean, THE table.
Spoiler
local t_LIST = {
"time",
"lua",
"print('Hello, there!')",
"exit()"
}
Last bit of code? (Wow, already?!)
Spoiler
for i=1,#t_LIST do
for n=1,#t_LIST[i] do
os.queueEvent("char",string.sub(t_LIST[i],n,n)
end
os.queueEvent("key",keys.enter)
end
"os.queueEvent(…)" is (as the name suggests) a function that when called queues an event. Whenever you press a button it queues both a "key" event and a "char" event (this can be seen more clearly with my Event Logger, EMAL), so all we need to do to make the shell think that we are entering all those commands is queue a bunch of char/key events. Keep in mind the differences between "char" and "key". "Char" refers to the character that relates to the key, so whenever I want to queue a key event for a letter I use the "char" event. Whenever I want to queue a key that isn't a letter, e.g. Space, Enter, Left Alt, 0, 1, 2 etc., I use the "key" event.
Whole code:
Spoiler
local t_LIST = {
"time",
"lua",
"print('Hello, there!')",
"exit()"
}
for i=1,#t_LIST do
for n=1,#t_LIST[i] do
os.queueEvent("char",string.sub(t_LIST[i],n,n)
end
os.queueEvent("key",keys.enter)
end
Part 2 - Adding different events
Spoiler
So we've discussed 'key' and 'char' events, creating a table that is run through to queue a bunch of key presses.So as you may have realised os.queueEvent(…) can spoof any event that your heart desires, and more. You can create custom events as well. E.g.
os.queueEvent("dummy",true)
local event, arg = os.pullEvent()
print(event, arg)
# Output: dummy, true
But what about other events, like maybe a spoofing a floppy drive being removed or added, mouse events (clicking, releasing, dragging etc.), pastes and even redstone changes. Well, yes, you can queue those events and make programs think that a user has just clicked on a button on the screen when they actually didn't.Well, to do all these we're going to need to make some changes to our code because at the moment it can only handle 'char' events. So, first of all, let's change up the table.
Spoiler
local t_LIST = {
[1] = {
eventType = "char",
eventArg = "lua",
endWithKey = keys.enter
},
[2] = {
eventType = "char",
eventArg = "print('Hello World!')",
endWithKey = keys.enter
},
[3] = {
eventType = "char",
eventArg = "exit()",
endWithKey = keys.enter
},
[4] = {
eventType = "char",
eventArg = "eventchecker",
endWithKey = keys.enter
},
[5] = {
eventType = "mouse_click",
eventArg = { 1, 2, 2 },
endWithKey = false
}
}
So you may notice the general layout of this table has changed..completely. This is because we need to tell the for loop more than just a string to write now. We need to also tell it what kind of event it's going to queue, what the arguments are (we don't want to queue a string for a mouse click do we?), and if we want to end with a key or not (just in case it is a "char" event with a string that needs to be entered into an input box).
Notice how the "mouse_click" event (number 5) has a table as an argument. This is because the 'mouse_click' event returns 3 arguments. The button clicked and the X and Y positions. So this particular event has the button set to '1' (left button clicked), X is 2 and Y is 2. So it clicked in at position (2,2) with the left button. Easy. Now we just need to tell the for loop to handle this as such.
Spoiler
for i=1,#t_LIST do
if type(t_LIST[i].eventArg) == "table" then
os.queueEvent(t_LIST[i].eventType,unpack(t_LIST[i].eventArg))
else
os.queueEvent(t_LIST[i].eventType,t_LIST[i].eventArg)
end
if t_LIST[i].endWithKey then
os.queueEvent("key",t_LIST[i].endWithKey)
end
end
So you may notice that we've gone from 2 for loops to 1, making the general flow a bit faster. What it's doing is it's going through each table in the 't_LIST' table. On line 2 we can see that it's checking if the 'eventArg' variable is a table or not. If it is, we need to unpack the table so that 'os.queueEvent(…)' can have all the arguments lined out as if it was like so:
os.queueEvent("mouse_click",unpack({1,2,2})) => os.queueEvent("mouse_click",1,2,2)
However, we need to make sure it doesn't unpack other 'types', like strings or numbers, so we have to put it in an if statement for the other 'case'. After this, we need to check if there is an 'endWithKey' key set. If there is, we can queue a 'key' event with that specific key as the argument. If it isn't set (refer to the "mouse_click" event table) and it's set to either 'false' or 'nil' then it won't press any key, or even queue the event.
It's as easy as that. It is a bit confusing to think that it's not running any programs before the program even finishes, but just remember it works.
Whole code:
Spoiler
local t_LIST = {
[1] = {
eventType = "char",
eventArg = "lua",
endWithKey = keys.enter
},
[2] = {
eventType = "char",
eventArg = "print('Hello World!')",
endWithKey = keys.enter
},
[3] = {
eventType = "char",
eventArg = "exit()",
endWithKey = keys.enter
},
[4] = {
eventType = "char",
eventArg = "eventchecker",
endWithKey = keys.enter
},
[5] = {
eventType = "mouse_click",
eventArg = { 1, 2, 2 },
endWithKey = false
}
}
for i=1,#t_LIST do
if type(t_LIST[i].eventArg) == "table" then
os.queueEvent(t_LIST[i].eventType,unpack(t_LIST[i].eventArg))
else
os.queueEvent(t_LIST[i].eventType,t_LIST[i].eventArg)
end
if t_LIST[i].endWithKey then
os.queueEvent("key",t_LIST[i].endWithKey)
end
end
Disadvantages of using the event queue to call programs:
- Queuing a program that uses "os.pullEvent()" will steal the next event in the event queue. So if, for example, you use a program that calls "os.pullEvent()" once, and you've told this program to run the event-pulling program and then "time" after it, it may only enter something like this:
ime
Where the event-pulling program steals the "char" event that has the argument "t". Keep in mind if you call a program that has a while-loop with "os.pullEvent()", it will keep stealing your events in the event queue until the program finishes. Not cool, I know. What is cool about it is you can pass events to that program - so if you know it's going to loop with "os.pullEvent()" you can queue specific events (e.g. mouse clicks for UIs, key presses etc. to navigate through a menu without you having to do anything), so it's cool in that sense.