This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Shazz's profile picture

multiMon - Multiple monitors in ComputerCraft.

Started by Shazz, 20 April 2014 - 04:53 PM
Shazz #1
Posted 20 April 2014 - 06:53 PM
multiMon v1.0

by Shazz



Intro
Hello everyone! Almost a year ago I released MonAPI which was an API that allowed you to make small sections within monitors that acted more or less like a regular monitor object. I had originally made this API for a project I was working on and decided to polish it up and release it. It worked but the code wasn't very pretty and it was missing some features (scroll, setCursorBlink). I had plans to implement these features but one thing I really wanted to see in the API was support for multiple monitors. Anyway, 1.6 was released and it came with a window API that basically did what my API did but it was a lot more efficient, feature-complete and better coded in general. It however did not have support for multiple monitors. Seeing this, it motivated me to work on my API again, however I wanted to just make a new API that did multiple monitors and nothing else. So that is why I am releasing multiMon.



Documentation
The API comes with 1 function:

create( name, monitors, [, wide [, tall [, width [, height ]]]] )
This functions creates a virtual monitor (that is fully compatible with the peripheral API and emits monitor_touch events) out of multiple monitor objects stitched together. It accepts the following arguments:
  • name Name of the virtual monitor.
  • monitors A table in format: { { object [, …] } [, …] } containing the monitor objects to be stitched.
  • wide Optional (default: determined by 'monitors' argument). Amount of monitor objects stacked horizontally.
  • tall Optional (default: determined by 'monitors' argument). Amount of monitor objects stacked vertically.
  • width Optional (default: the width of the first object in 'monitors' argument). Width of each monitor object.
  • height Optional (default: the height of the first object in 'monitors' argument). Height of each monitor object.
This function returns a handle to the virtual monitor created. Same as calling peripheral.wrap(name).

Side notes:
1. All monitors should be of the width and height and the monitors stitched together should form a rectangle.
2. You must load the API [os.loadAPI("multiMon")] before wrapping to the actual monitors otherwise touch events may not work.
3. This API is meant to be used with monitors however it should be compatible with terminal and window objects.



Examples
Setup:
Spoiler
Yeah, that's right 9 full size monitors. Here is what the back looks like:

Simple example:
Spoiler

--# Load the API
os.loadAPI('/multiMon')
--# Create the monitors object (the monitors are arranged in the world the same way you see them in the table below).
local monitorSetup = {
  {peripheral.wrap('monitor_22'), peripheral.wrap('monitor_23'), peripheral.wrap('monitor_24')},
  {peripheral.wrap('monitor_25'), peripheral.wrap('monitor_26'), peripheral.wrap('monitor_27')},
  {peripheral.wrap('monitor_19'), peripheral.wrap('monitor_20'), peripheral.wrap('monitor_21')},
}

--# Create the virtual monitor (will also return the handle).
local disp = multiMon.create('myMonitor', monitorSetup)

--# Showcasing some basic functions.
disp.setTextScale(0.5)
if disp.isColour() then
  disp.setTextColour(colours.white)
  disp.setBackgroundColour(colours.black)
end
disp.setCursorBlink(false)
disp.clear()
disp.setCursorPos(1, 1)
local width, height = disp.getSize()

--# Writes random characters onto the screen.
for y = 1, height do
  disp.setCursorPos(1, y)
  disp.write(string.rep(string.char(math.random(32, 126)), width))
end
Here is what it looks like on the monitor:

Here it is with 0.5 text scale instead of 5:

Closer up:

Running paint (on the same setup):
Spoiler

--# Load the API
os.loadAPI('/multiMon')

--# Create the monitors object (the monitors are arranged in the world the same way you see them in the table below).
local monitorSetup = {
  {peripheral.wrap('monitor_22'), peripheral.wrap('monitor_23'), peripheral.wrap('monitor_24')},
  {peripheral.wrap('monitor_25'), peripheral.wrap('monitor_26'), peripheral.wrap('monitor_27')},
  {peripheral.wrap('monitor_19'), peripheral.wrap('monitor_20'), peripheral.wrap('monitor_21')},
}

--# Create the virtual monitor (will also return the handle).
local disp = multiMon.create('myMonitor', monitorSetup)

--# Showcasing some basic functions.
disp.setTextScale(0.5)
if disp.isColour() then
  disp.setTextColour(colours.white)
  disp.setBackgroundColour(colours.black)
end
disp.setCursorBlink(false)
disp.clear()
disp.setCursorPos(1, 1)
local width, height = disp.getSize()

--# Some setup is needed for paint.
parallel.waitForAny(
  function()
   --# Since paint is meant to work only on the terminal, we need to catch all touch events and turn them into mouse_click events.
   --# This happens in the background while paint is running.
   while true do
	local ev = {os.pullEvent('monitor_touch')}
	if ev[2] == 'myMonitor' then
	 os.queueEvent('mouse_click', 1, ev[3], ev[4])
	end
   end
  end,
  function()
   --# Since paint uses 'term' internally, we need to set 'disp' to act as 'term'.
   term.redirect(disp)
   --# Run the program with 'image' as the argument.
   shell.run('/rom/programs/advanced/paint', 'image')
  end
)
Empty canvas:

After some random brush strokes:

Playing worm (on the same setup):
Spoiler

--# Load the API
os.loadAPI('/multiMon')

--# Create the monitors object (the monitors are arranged in the world the same way you see them in the table below).
local monitorSetup = {
  {peripheral.wrap('monitor_22'), peripheral.wrap('monitor_23'), peripheral.wrap('monitor_24')},
  {peripheral.wrap('monitor_25'), peripheral.wrap('monitor_26'), peripheral.wrap('monitor_27')},
  {peripheral.wrap('monitor_19'), peripheral.wrap('monitor_20'), peripheral.wrap('monitor_21')},
}

--# Create the virtual monitor (will also return the handle).
local disp = multiMon.create('myMonitor', monitorSetup)

--# Showcasing some basic functions.
disp.setTextScale(2)
if disp.isColour() then
  disp.setTextColour(colours.white)
  disp.setBackgroundColour(colours.black)
end
disp.setCursorBlink(false)
disp.clear()
disp.setCursorPos(1, 1)
local width, height = disp.getSize()

--# Since worm uses 'term' internally, we need to set 'disp' to act as 'term'.
term.redirect(disp)

--# Run the program.
shell.run('/rom/programs/fun/worm')




Download
pastebin get Yu9jAVuk multiMon



Changelog
Spoilerv1.0
- Initial release
Zambonie #2
Posted 20 April 2014 - 07:09 PM
Seems Nice!
I Really like the worm Example.
This gives me an Idea :)/>
MKlegoman357 #3
Posted 20 April 2014 - 07:09 PM
WOW, I actually did something really similar to this (it's on Grim's server), but it had to be rewritten because of scrolling. I even chose the same name for my program, but my version would have had a lot more features than yours have, huh, maybe I'll make it one day…

But nice job doing this, glad I'm not the only one who has made something like this :D/>.
viluon #4
Posted 21 April 2014 - 03:29 PM
Cool! That's very useful :)/>
Shazz #5
Posted 21 April 2014 - 09:12 PM
Seems Nice!
I Really like the worm Example.
This gives me an Idea :)/>

It can actually go bigger (or smaller depending on how you look at it) if you set the text scale to 0.5 :P/>
Zambonie #6
Posted 21 April 2014 - 09:37 PM
Haha. That's what I accutly did. I tried making a .. about 460*380 Display.
Though it doesn't look like a real Display, Cause CC's pixels are shaped like rectangles.
Also, I was really getting about 1- 5 FPS on Max Ram and lowest Graphic detail settings when the monitor was on. About 60- 260 FPS is ussaly what I get. (Proboly 115 The most)
But it works, anyways ;)/>
Shazz #7
Posted 23 April 2014 - 09:43 PM
WOW, I actually did something really similar to this (it's on Grim's server), but it had to be rewritten because of scrolling. I even chose the same name for my program, but my version would have had a lot more features than yours have, huh, maybe I'll make it one day…

But nice job doing this, glad I'm not the only one who has made something like this :D/>.

What a coincidence, especially the name. Any features you would like to see in this version?
MKlegoman357 #8
Posted 23 April 2014 - 09:56 PM
WOW, I actually did something really similar to this (it's on Grim's server), but it had to be rewritten because of scrolling. I even chose the same name for my program, but my version would have had a lot more features than yours have, huh, maybe I'll make it one day…

But nice job doing this, glad I'm not the only one who has made something like this :D/>/>.

What a coincidence, especially the name. Any features you would like to see in this version?

Anything you can imagine :D/>. Maybe a program with which you could redirect terminal to the monitor, auto-adjusting and centering the redirected output to the center of the giant monitor. Also, does this support non-advanced monitors? What if I would make one monitor advanced and the other one non-advanced? Would it act like an advanced or not?
Shazz #9
Posted 24 April 2014 - 10:08 PM
Anything you can imagine :D/>. Maybe a program with which you could redirect terminal to the monitor, auto-adjusting and centering the redirected output to the center of the giant monitor. Also, does this support non-advanced monitors? What if I would make one monitor advanced and the other one non-advanced? Would it act like an advanced or not?

Those are great ideas but I would like this to be just a simple API which mimics a regular monitor object. And yes, it does support non-advanced monitors. If there is at least 1 non-advanced monitor then the virtual monitor will act like a non-advanced monitor.
Sir_Mr_Bman #10
Posted 24 April 2014 - 11:01 PM
Love the idea!

Now to expand the wall of my base to make worm more fun… hehehe…
KingofGamesYami #11
Posted 02 May 2014 - 12:40 AM
This is Amazing! However, I do have some suggestions:

-Remove the name part in the create function, I don't see why it's needed and is really annoying when it error saying the monitor already exists
-Since you are already overriding all the commands, add these: disp.getTextScale, disp.getBackgroundColor, and disp.getTextColor.
-if the name isn't removed, or you use OO, an "add monitor" function could be useful. Also a way to remove an already created (virtual) monitor.
Edited on 01 May 2014 - 10:41 PM
Shazz #12
Posted 03 May 2014 - 06:57 PM
This is Amazing! However, I do have some suggestions:

-Remove the name part in the create function, I don't see why it's needed and is really annoying when it error saying the monitor already exists
-Since you are already overriding all the commands, add these: disp.getTextScale, disp.getBackgroundColor, and disp.getTextColor.
-if the name isn't removed, or you use OO, an "add monitor" function could be useful. Also a way to remove an already created (virtual) monitor.

- The name specifies the virtual peripheral's name, so you can use peripheral.* functions and it's also used for monitor_touch events. (I might make the name optional and disable peripheral.* and monitor_touch events if it's not specified.)
- Good idea, will add those in the next version.
- I'm not sure what you mean by "add monitor" but I will look into adding a way to remove a virtual monitor.
Edited on 03 May 2014 - 04:57 PM
KingofGamesYami #13
Posted 04 May 2014 - 06:12 PM
- I'm not sure what you mean by "add monitor" but I will look into adding a way to remove a virtual monitor.
I meant that you could add another monitor to the virtual monitor. Say I have this setup:
M M M
M M M
and I want to make it
M M M M
M M M M
The name specifies the virtual peripheral's name, so you can use peripheral.* functions and it's also used for monitor_touch events. (I might make the name optional and disable peripheral.* and monitor_touch events if it's not specified.)
Yes, but in the example you have it returning a value anyway. Using OO (not saying you should) you could make the virtual monitor anything you want, and it would return a pointer to it.
Edited on 04 May 2014 - 04:34 PM
Shazz #14
Posted 04 May 2014 - 09:07 PM
I meant that you could add another monitor to the virtual monitor. Say I have this setup:
M M M
M M M
and I want to make it
M M M M
M M M M
I'll look into that.

Yes, but in the example you have it returning a value anyway. Using OO (not saying you should) you could make the virtual monitor anything you want, and it would return a pointer to it.
The whole point of this API is to create a virtual monitor that acts the same way as a real monitor attached as a peripheral would. The reasoning behind this is so that programs that use the peripheral.* will work properly. Also, the name is used for monitor_touch events, since a monitor_touch event needs to emit the name of the monitor along with the x & y. The fact that the create function returns the handle is just for convenience; so you wouldn't have to do:

create('mMon', ...)
local myMon = peripheral.wrap('mMon')
instead you could just do this:

local myMon = create('mMon', ...)
In fact, if you look at the API code, you will see:

return peripheral.wrap(name)
KingofGamesYami #15
Posted 04 May 2014 - 11:22 PM
The whole point of this API is to create a virtual monitor that acts the same way as a real monitor attached as a peripheral would. The reasoning behind this is so that programs that use the peripheral.* will work properly. Also, the name is used for monitor_touch events, since a monitor_touch event needs to emit the name of the monitor along with the x & y. The fact that the create function returns the handle is just for convenience; so you wouldn't have to do:

create('mMon', ...)
local myMon = peripheral.wrap('mMon')
instead you could just do this:

local myMon = create('mMon', ...)
In fact, if you look at the API code, you will see:

return peripheral.wrap(name)
I get that you are making a virtual monitor, I am saying you should create your own name inside the api, instead of having the programmer name it themselves.
apemanzilla #16
Posted 05 May 2014 - 02:36 AM
The whole point of this API is to create a virtual monitor that acts the same way as a real monitor attached as a peripheral would. The reasoning behind this is so that programs that use the peripheral.* will work properly. Also, the name is used for monitor_touch events, since a monitor_touch event needs to emit the name of the monitor along with the x & y. The fact that the create function returns the handle is just for convenience; so you wouldn't have to do:

create('mMon', ...)
local myMon = peripheral.wrap('mMon')
instead you could just do this:

local myMon = create('mMon', ...)
In fact, if you look at the API code, you will see:

return peripheral.wrap(name)
I get that you are making a virtual monitor, I am saying you should create your own name inside the api, instead of having the programmer name it themselves.
To be honest, I believe it would be better the way it is now as it allows use of other overriden peripheral functions - find, getNames, etc.
Edited on 05 May 2014 - 12:36 AM
KingofGamesYami #17
Posted 05 May 2014 - 10:12 PM
-snip-
To be honest, I believe it would be better the way it is now as it allows use of other overriden peripheral functions - find, getNames, etc.
I don't mean change anything fundamental, all I was thinking was generating it's own name. Instead of you using create(name, monitors), use create(monitors) which returns name. You could use disp = peripheral.wrap(create(monitors)) to get the wrapped peripheral. It was a way to eliminate this error:

peripheral already exists
but this would be irrelevant if a way to delete a disp was added.
apemanzilla #18
Posted 07 May 2014 - 02:25 PM
-snip-
To be honest, I believe it would be better the way it is now as it allows use of other overriden peripheral functions - find, getNames, etc.
I don't mean change anything fundamental, all I was thinking was generating it's own name. Instead of you using create(name, monitors), use create(monitors) which returns name. You could use disp = peripheral.wrap(create(monitors)) to get the wrapped peripheral. It was a way to eliminate this error:

peripheral already exists
but this would be irrelevant if a way to delete a disp was added.
In that case, why not make the argument optional? If it's missing or nil, a name is generated, otherwise it uses the passed argument.
KingofGamesYami #19
Posted 08 May 2014 - 02:08 AM
-snip-
You could do that, but I was thinking more of if you loop a section of code, and it happens to create the monitor new each time or something similar.
apemanzilla #20
Posted 11 May 2014 - 04:20 PM
-snip-
You could do that, but I was thinking more of if you loop a section of code, and it happens to create the monitor new each time or something similar.
That would still work fine. Just leave out the argument and it automatically generates the name for you. And if you want to set a name yourself, add the extra argument.