7

In Windows, winapi provides a function that reports information about a monitor:

DEVMODE dm;
dm.dmSize = sizeof(DEVMODE);

EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);

int FPS = dm.dmDisplayFrequency;

What is the equivalent of this on Linux? The Linux man pages direct me to an allegro library function, but not only am I not using allegro, that function is from a very outdated version of said library and reportedly only works on Windows.

Iwan Aucamp
  • 1,469
  • 20
  • 21
NmdMystery
  • 2,778
  • 3
  • 32
  • 60
  • sudo ddcprobe | grep monitorrange – Stefan Jul 22 '13 at 21:44
  • So basically execlp grep? Does this work on all distros, or at least the majority of them? – NmdMystery Jul 22 '13 at 21:46
  • as far as i know on most of the available distros. if you're on debian based try: sudo apt-get install xresprobe – Stefan Jul 22 '13 at 21:48
  • after that you can run: sudo ddcprobe | grep monitorrange to get the horizontal- and vertical refresh rate... – Stefan Jul 22 '13 at 21:50
  • I actually need a way to do this so that the FPS is stored in the program rather than output the FPS to a terminal. execlp actually won't relay the information back to the program that calls it, so is there a different function that does? – NmdMystery Jul 22 '13 at 21:52
  • @NmdMystery Now that you know the program's name you can dig in its source and see how it does it. Ah, the beauty of open-source software... ;) – syam Jul 22 '13 at 21:58
  • Linux itself has little windowing support. X Windows does the GUI stuff. Use popen() to read the ddcprobe output in your program. – brian beuning Jul 22 '13 at 21:58
  • @syam Oh yeah! Kind of forgot that's why I moved to Linux in the first place XD – NmdMystery Jul 22 '13 at 22:05
  • @brian beuing I thought I read somewhere that X Window doesn't give you this information... not sure why. I'll try that. – NmdMystery Jul 22 '13 at 22:06

3 Answers3

6

Use XRandr API (man 3 Xrandr). See here for an example:

You can also look at the code for xrandr(1).


Edit1: For posterity sake:

Sample code slightly adjusted so its more of a demo:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>

int main()
{
    int num_sizes;
    Rotation current_rotation;

    Display *dpy = XOpenDisplay(NULL);
    Window root = RootWindow(dpy, 0);
    XRRScreenSize *xrrs = XRRSizes(dpy, 0, &num_sizes);
    //
    //     GET CURRENT RESOLUTION AND FREQUENCY
    //
    XRRScreenConfiguration *conf = XRRGetScreenInfo(dpy, root);
    short current_rate = XRRConfigCurrentRate(conf);
    SizeID current_size_id = XRRConfigCurrentConfiguration(conf, &current_rotation);

    int current_width = xrrs[current_size_id].width;
    int current_height = xrrs[current_size_id].height;
    std::cout << "current_rate = " << current_rate << std::endl;
    std::cout << "current_width = " << current_width << std::endl;
    std::cout << "current_height = " << current_height << std::endl;

    XCloseDisplay(dpy);
}

Compile with:

g++ 17797636.cpp -o 17797636 -lX11 -lXrandr

Output:

$ ./17797636 
current_rate = 50
current_width = 1920
current_height = 1080
Iwan Aucamp
  • 1,469
  • 20
  • 21
  • 1
    What if you have two monitors with extended desktop? In my case it only prints my laptop refresh rate, not my external display's. – user3111525 Nov 01 '16 at 06:18
  • 1
    This now provides me with an incorrect refresh rate; it says 50 Hz even though my rate is 60 Hz. See https://stackoverflow.com/a/38222346/1364776 for a little more information. I get 50 out of xrandr --q1 but 60 out of xrandr --q12 (and every other tool I try), just like that person did. I don't know yet what the right refresh rate reading code is though... The answer I linked optimistically says that xrandr.c should tell how to do it, which I guess it must, but that code is too complicated and fragile-looking for me to rip out just the right chunks of it. – mjwach Nov 04 '19 at 09:03
2

A simple example:

#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>

int main(int argc, char *argv[])
{
  Display *display = XOpenDisplay(NULL);
  Window default_root_window = XDefaultRootWindow(display);

  XRRScreenResources *screen_resources = XRRGetScreenResources(display, default_root_window);

  RRMode active_mode_id = 0;
  for (int i = 0; i < screen_resources->ncrtc; ++i) {
    XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(display, screen_resources, screen_resources->crtcs[i]);
    // If None, then is not displaying the screen contents
    if (crtc_info->mode != None) {
      active_mode_id = crtc_info->mode; 
    }
  }

  double active_rate = 0;
  for (int i = 0; i < screen_resources->nmode; ++i) {
    XRRModeInfo mode_info = screen_resources->modes[i];
    if (mode_info.id == active_mode_id) {
      active_rate = (double)mode_info.dotClock / ((double)mode_info.hTotal * (double)mode_info.vTotal);
    }
  }

  printf("Active rate is: %.1f\n", active_rate);

  return 0;
}
  • this appears to be the only correct answer, though it doesn't handle the case where there are multiple outputs for the same X screen (in which case where will be more than one "active mode") – Eevee Feb 06 '22 at 14:29
1

Iwan's answer did not work for me; xrandr has changed since 2013 I guess? The command-line tool xrandr can read my refresh rate correctly, but its source code is too complex for me to be willing to copy the way it's doing so. Instead I have chosen to clumsily delegate the work to the entire xrandr program. My crappy solution is pasted below.

Note that this solution is likely to be unreliable when multiple display devices are connected, and will probably someday break when xrandr changes again.

(pstream.h is provided by Jonathan Wakely's PStreams library, referenced here: https://stackoverflow.com/a/10702464/1364776

I'm only using it to turn the output of a command into a std::string; obviously there are various other ways to do that so use one of them if you prefer.)

#include <pstream.h>
#include <cctype>
#include <cstdlib>
#include <cmath>

float getRefreshRate()
{
    try
    {
        redi::ipstream queryStream("xrandr");
        std::string chunk;
        while (queryStream >> chunk)
        {
            auto rateEnd = chunk.find("*");
            if (rateEnd != std::string::npos)
            {
                auto rateBeginning = rateEnd;
                while (std::isdigit(chunk[rateBeginning]) || std::ispunct(chunk[rateBeginning]))
                    --rateBeginning;
                ++rateBeginning;

                auto numberString = chunk.substr(rateBeginning, rateEnd - rateBeginning);
                float rate = std::strtof(numberString.data(), nullptr);
                if (rate != 0 && rate != HUGE_VALF)
                    return rate;
            }
        }
    }
    catch (...)
    {
    }

    return 60; // I am not proud of any of this :(
}
mjwach
  • 1,174
  • 2
  • 9
  • 25
  • 1
    (I hope that my real, long-term solution to this problem in my current project will be to forget all this nonsense and just ask Qt for the refresh rate, which appears to be something one may do via QScreen or some such. But I won't offer that as a proper answer to the original question because obviously not every project is using Qt.) – mjwach Nov 04 '19 at 10:31