├── .gitignore ├── README.md ├── meson.build ├── LICENSE └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /build-*/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drm_monitor 2 | 3 | A CLI tool to monitor DRM/KMS state. 4 | 5 | > ### ⚠️ Migrated to gitlab.freedesktop.org 6 | > 7 | > This project has [migrated to gitlab.freedesktop.org](https://gitlab.freedesktop.org/emersion/drm_monitor). 8 | 9 | ## Building 10 | 11 | Requires libdrm. 12 | 13 | meson build/ 14 | ninja -C build/ 15 | 16 | ## License 17 | 18 | MIT 19 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'drm_monitor', 3 | 'c', 4 | license: 'MIT', 5 | meson_version: '>=0.49.0', 6 | default_options: [ 7 | 'c_std=c11', 8 | 'warning_level=2', 9 | 'werror=true', 10 | ], 11 | ) 12 | 13 | cc = meson.get_compiler('c') 14 | 15 | add_project_arguments(cc.get_supported_arguments([ 16 | '-Wno-missing-braces', 17 | '-Wno-missing-field-initializers', 18 | '-Wno-unused-parameter', 19 | ]), language: 'c') 20 | 21 | libdrm = dependency('libdrm') 22 | 23 | executable( 24 | 'drm_monitor', 25 | files('main.c'), 26 | dependencies: [libdrm], 27 | install: true, 28 | ) 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Simon Ser 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | static const char ESC = 0x001B; 15 | 16 | struct crtc { 17 | uint32_t id; 18 | 19 | uint64_t seq; 20 | uint64_t ns; 21 | 22 | uint64_t delta_seq; 23 | uint64_t delta_ns; 24 | }; 25 | 26 | static struct crtc crtcs[64] = {0}; 27 | static size_t crtcs_len = 0; 28 | 29 | static int monitor_crtc(int fd, struct crtc *crtc) { 30 | uint32_t queue_flags = DRM_CRTC_SEQUENCE_RELATIVE | 31 | DRM_CRTC_SEQUENCE_NEXT_ON_MISS; 32 | int ret = drmCrtcQueueSequence(fd, crtc->id, queue_flags, 33 | 1, NULL, (uint64_t)crtc); 34 | if (ret != 0 && errno != EINVAL) { 35 | perror("drmCrtcQueueSequence"); 36 | return ret; 37 | } 38 | return 0; 39 | } 40 | 41 | static void print_state(void) { 42 | static bool first = true; 43 | if (!first) { 44 | for (size_t i = 0; i < crtcs_len; i++) { 45 | printf("%c[1A", ESC); // move up 46 | printf("%c[2K", ESC); // clear line 47 | } 48 | } 49 | first = false; 50 | 51 | for (size_t i = 0; i < crtcs_len; i++) { 52 | struct crtc *crtc = &crtcs[i]; 53 | double rate = 0; 54 | if (crtc->delta_ns > 0) { 55 | rate = 1000000000.0 / crtc->delta_ns; 56 | } 57 | printf("CRTC %"PRIu32": seq=%"PRIu64" ns=%"PRIu64" delta_ns=%"PRIu64" Hz=%f\n", 58 | crtc->id, crtc->seq, crtc->ns, crtc->delta_ns, rate); 59 | } 60 | } 61 | 62 | static void handle_sequence(int fd, uint64_t seq, uint64_t ns, uint64_t data) { 63 | struct crtc *crtc = (struct crtc *)data; 64 | assert(seq > crtc->seq); 65 | assert(ns > crtc->ns); 66 | crtc->delta_seq = seq - crtc->seq; 67 | crtc->delta_ns = ns - crtc->ns; 68 | crtc->seq = seq; 69 | crtc->ns = ns; 70 | 71 | print_state(); 72 | 73 | monitor_crtc(fd, crtc); 74 | } 75 | 76 | static const char usage[] = 77 | "Usage: drm_monitor [options...]\n" 78 | "\n" 79 | " -d Specify DRM device (default /dev/dri/card0).\n" 80 | " -h Show help message and quit.\n"; 81 | 82 | int main(int argc, char *argv[]) { 83 | char *device_path = "/dev/dri/card0"; 84 | int opt; 85 | while ((opt = getopt(argc, argv, "hd:")) != -1) { 86 | switch (opt) { 87 | case 'h': 88 | printf("%s", usage); 89 | return EXIT_SUCCESS; 90 | case 'd': 91 | device_path = optarg; 92 | break; 93 | default: 94 | return EXIT_FAILURE; 95 | } 96 | } 97 | int fd = open(device_path, O_RDONLY); 98 | if (fd < 0) { 99 | perror("open"); 100 | return 1; 101 | } 102 | 103 | drmModeRes *res = drmModeGetResources(fd); 104 | if (res == NULL) { 105 | perror("drmModeGetResources"); 106 | return 1; 107 | } 108 | assert((size_t)res->count_crtcs < sizeof(crtcs) / sizeof(crtcs[0])); 109 | 110 | crtcs_len = (size_t)res->count_crtcs; 111 | for (int i = 0; i < res->count_crtcs; i++) { 112 | struct crtc *crtc = &crtcs[i]; 113 | crtc->id = res->crtcs[i]; 114 | 115 | int ret = drmCrtcGetSequence(fd, crtc->id, &crtc->seq, &crtc->ns); 116 | if (ret != 0 && errno != EINVAL) { 117 | // EINVAL can happen if the CRTC is disabled 118 | perror("drmCrtcGetSequence"); 119 | return 1; 120 | } 121 | 122 | if (monitor_crtc(fd, crtc) != 0) { 123 | return 1; 124 | } 125 | } 126 | 127 | drmModeFreeResources(res); 128 | 129 | print_state(); 130 | 131 | while (1) { 132 | drmEventContext ctx = { 133 | .version = 4, 134 | .sequence_handler = handle_sequence, 135 | }; 136 | if (drmHandleEvent(fd, &ctx) != 0) { 137 | perror("drmHandleEvent"); 138 | return 1; 139 | } 140 | } 141 | 142 | close(fd); 143 | return 0; 144 | } 145 | --------------------------------------------------------------------------------