0095: Hardware II – Full Monitor Report

This time, we’re looking for more than just a monitor head count. We can get the resolution of each monitor in a multi-monitor Seat, each monitor’s position within the Screen (or Seat, if you prefer), the model and, since we’ll have all the information we’ll need at our fingertips, let’s also find out which monitor our Window is on at any given time. This fulfills a request made by GreatSam4Sure on forum.dlang.org.

Window Location & Other Stuff

Results of this example:
Current example output
Current example output
Current example terminal output
Current example terminal output (click for enlarged view)

To accomplish all this, we’re going to need a whole raft of imports, so as well as the ones we used from last time, we need to add these:

import gtk.Button;

import gdk.Device;
import gdk.Seat;
import gdk.Window;
import gdk.Rectangle;
import gdk.MonitorG;
import gdk.Screen;

The reason we need the first import is obvious. The Button created will, when pressed, tell us which monitor the window is on. The rest are for gathering the information we want and we’ll go over them as we come to them.

TestRigWindow Class Preamble

Here in the class preamble, we get set up to gather the info:

Screen screen;
ReportButton reportButton;

How they’re used:

  • screen: the default Screen used by our application,
  • reportButton: which, as stated above, will be used to tell us which monitor the Window is on at any given time. (If you’ve got a multi-monitor Seat, try moving the window from one to another and then clicking the button.)

The Constructor

Here, we also have a bit more to do:

this()
{
	super(title);

	myDisplay = Display.getDefault();
	monitorReport();

	screen = Screen.getDefault();
	writeln("screen width: ", screen.width(), ", height: ", screen.height());

	reportButton = new ReportButton(myDisplay);
	add(reportButton);

	addOnDestroy(&quitApp);

	showAll();

} // this()

Besides calling monitorReport() to dump all the monitor information to the terminal, we also:

  • grab a pointer to the default Screen (from which all the other info is gleaned), and
  • instantiate the reportButton, passing it a pointer to the default Display.

We don’t have to pass this pointer. Because the Display is the default Display, it’s accessible from anywhere within our application. And that means we could just grab a pointer to the default Display from within the reportButton constructor instead. Either way works, but…

For the TestRigWindow.monitorReport() function to work, we need a pointer to the default Display there as well, so we might as well leave it there in the TestRigWindow constructor and pass it to the ReportButton. Then if, sometime down the road, we decide to change the way we’re handling the Display pointer, we only have to rework it in one place.

The monitorReport() Function

Here’s the function where it all happens:

void monitorReport()
{
	MonitorG monitorG;
	GdkRectangle rectangle;
	int numberOfMonitors = myDisplay.getNMonitors();

	writeln("Your set-up has ", numberOfMonitors, " monitors...\n\n");

	if(numberOfMonitors > 1)
	{
		foreach(int i; 0..numberOfMonitors)
		{
			monitorG = myDisplay.getMonitor(i);
			monitorG.getGeometry(rectangle);

			writeln("monitor #", i);
			writeln("monitor position within the screen area - x = ", rectangle.x, ", y = ", rectangle.y);
			writeln("monitor size: width = ", rectangle.width, ", height = ", rectangle.height);
			writeln("manufacturer: ", monitorG.getManufacturer());
			write("monitor model: ", monitorG.getModel());

			if(monitorG.isPrimary())
			{
				write(" and it's your primary display.");
			}

			writeln();
			writeln();
		}
	}
	else
	{
		monitorG = myDisplay.getMonitor(0);

		writeln("You have a single monitor");
		writeln("monitor position within the display - x = ", rectangle.x, ", y = ", rectangle.y);
		writeln("monitor size: width = ", rectangle.width, ", height = ", rectangle.height);
		writeln("manufacturer: ", monitorG.getManufacturer());
		write("monitor model: ", monitorG.getModel());

		writeln(" and it's your only display.");
	}

} // monitorReport()

This function starts off the same as it did in our previous example, by finding out how many monitors are available to the Seat, but from there, it goes into more detail, but before we go there, let’s look at the variables in the function preamble and what they’re used for:

  • int numberOfMonitors: self-explanitory,
  • monitorG: a temporary pointer for each monitor as we loop through the list of monitors in monitorReport(), and
  • rectangle: each monitor’s geometry (screen size and its placement in the Screen) as we loop through the list.

Looking at the big picture here, we first determine if we’re on a single-monitor Seat. If not, we dig in and:

  • grab a pointer to the current monitor, assigning it to monitorG,
  • find it’s geometry and store that info in a rectangle struct (GdkRectangle is defined in generated/gtkd/gdk/c/types.d starting on line #3933),
  • as each monitor is examined, we look to see if it’s the primary monitor for the Seat, and
  • push all these various bits of information to the terminal.

However, if there’s only one monitor, we don’t need to dig around so much. Instead, we just grab a pointer to the single monitor and dump all its info to the terminal.

The ReportButton

A lot of this is similar to any Button we’ve worked with before. It’s got a string to name the Button, we make a size request (for convenience, really; it’s just so the full Window title shows and there’s something to grab onto when moving the Window from monitor to monitor), add a callback, and connect that callback to a signal.

So, let’s go over what we haven’t done before…

Preamble

We declare:

Display _myDisplay;
MonitorG _monitorG;

Since we’ve gone over these already, let’s just move on to…

Constructor

this(Display myDisplay)
{
	super(labelText);
	setSizeRequest(250, 30);

	addOnClicked(&onClicked);

	_myDisplay = myDisplay;

} // this()

We add a line to assign our local copy of the default Display pointer.

Things get interesting when we look at…

Callback

void onClicked(Button b)
{
	MonitorG monitorG4Window;
	monitorG4Window = _myDisplay.getMonitorAtWindow(this.getWindow());

	int numberOfMonitors = _myDisplay.getNMonitors();

	foreach(int i; 0..numberOfMonitors)
	{
		_monitorG = _myDisplay.getMonitor(i);

		if(_monitorG is monitorG4Window)
		{
			writeln("The current window is on monitor #", i);
		}
	}

} // onClicked()
  • monitorG4Window: reports whichever monitor the Window is on at the time the Button is pressed which is determined by a call to:
  • _myDisplay.getMonitorAtWindow() and passing in a pointer to our application’s Window,
  • then we go through the process once again of finding out how many monitors there are and grabbing a pointer to each so we can compare that pointer to the monitorG4Window to see if they match, and
  • write the result to the terminal.

Conclusion

And that leaves just one more thing to say before closing:

Happy holidays!

See you next time for more hardware stuff when we look at keyboards, including an alternate way to grab key presses.

See you then.

Comments? Questions? Observations?

Did we miss a tidbit of information that would make this post even more informative? Let's talk about it in the comments.

You can also subscribe via RSS so you won't miss anything. Thank you very much for dropping by.

© Copyright 2023 Ron Tarrant