Library / SDK
smasherprog/screen_capture_lite avatar
smasherprog/screen_capture_lite

screen_capture_lite: a callback driven screen and window capture library

cross platform screen/window capturing library

727 stars163 forksCMIT

At a glance

What is it?
screen_capture_lite is a C library that captures monitors, windows and mouse activity on Windows, macOS and Linux, and delivers raw BGRA frames through callbacks you choose. Its design pushes the work of enumeration and filtering onto you, and the README is blunt about what happens if you subscribe to events you do not need.
Who is it for?
Adopt screen_capture_lite when you are writing C or C++ on Windows, macOS or Linux and need screen, window or cursor content with a per target callback model, and you want to avoid maintaining three platform backends yourself. Do not adopt it if you need a stable tagged release, because the README states that master is where development happens and should not be considered stable, or if you need encoded output, since the frames arrive as raw pixels.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 172 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Monitors, windows and the mouse, in one capture session

screen_capture_lite is a cross platform screen and window capturing library written in C, with C# bindings alongside it. The problem it solves is narrow but common: getting pixels off the screen in a way that works the same on Windows, macOS and Linux, without each platform needing its own capture backend.

The library covers three things. It enumerates monitors and windows, it captures their contents, and it reports mouse movement and mouse image changes. The supported platforms listed in the README are Windows 7 and up, macOS and Linux, and the header comment claims no external dependencies except on Linux, where four development packages are needed.

Because the library is a source level C component rather than a service, the person integrating it decides what is captured. You build a capture configuration from a list of targets, attach the callbacks you want, and start the session. The targets come from the library's own enumeration functions, and you can pass all monitors, all windows, or a filtered subset you compute yourself.

Subscribe only to the events you actually want

The usage rules in the README are unusually emphatic, and they are the most useful part of the documentation. Only define the callbacks you are interested in, and if you define onMouseChanged the library will assume you want mouse information and monitor for it. The text repeats the point in caps: do not define callbacks for events you do not care about, because the library will do extra work assuming you want the information.

There is a second rule that catches people out. The library owns all image data, so if you want to use an image after the callback has completed you must copy the data out. The buffer belongs to the capture thread, and there is no lifetime guarantee once your callback returns.

The threading model is per target. Each monitor or window runs in its own thread, so there is no blocking and no internal synchronization between them, and capturing three monitors means a thread capturing each one. The consequence for your code is that the same callback can be entered concurrently for different targets, so any shared state of yours needs its own locking.

The two interfaces behave differently. Calls to ICaptureConfiguration cannot be changed once start_capturing has been called, so a configuration object is fixed at start and has to be destroyed and recreated to change it. Calls to IScreenCaptureManager are thread safe and can be made at any time from any thread, which is where the interval setters live.

Raw BGRA frames, and the alpha channel exception

The pixel format is raw BGRA, 32 bits per pixel, and the README walks through the byte order. Marching through the data with a for loop gives you [A,R,G,B], [A,R,G,B] and so on, and a link is given to a Stack Overflow post explaining the reasoning behind that order.

The interesting detail is where alpha means something. Alpha is unused for onNewFrame and onFrameChanged, but for onMouseChanged it is used. That is not a cosmetic difference: a cursor is composited onto the frame it sits on, and the alpha channel is what tells the consumer which pixels belong to the pointer. A renderer that ignores alpha will draw a black box around the cursor, while a consumer of the plain frame callbacks can safely treat the fourth byte as padding.

The three callbacks also differ in when they fire. onNewFrame calls back when a new frame is ready on the interval given by SCL_SetFrameChangeInterval. onFrameChanged calls back only when differences are detected between the last frame and the current one, which the README describes as useful when you want to stream only what changed rather than everything. onMouseChanged calls back when the mouse has changed location or the mouse image has changed, up to the maximum rate set by SCL_SetMouseChangeInterval.

Building it and the Linux packages you need first

The repository is a CMake project, with CMakeLists.txt and a cmake_uninstall.cmake.in at the root, and a .clang-format file for consistent formatting of the sources. The build output is orchestrated by azure-pipelines.yml with an azure-devops directory, and the README carries a build status badge for the Windows, Linux and Mac targets.

On Linux the README lists the development packages required, which are the X11 and XTest family: libxtst-dev, libxinerama-dev, libx11-dev and libxfixes-dev, installed with apt-get. That is the whole dependency story on Linux, and the README frames it as the only exception to having no external dependencies.

The examples directory shows how far the bindings reach. Example_CPP and Example_CSharp cover the two language bindings directly, and there are also Example_OpenGL and Example_Unity directories, which indicate that the frame data is meant to be handed straight to a renderer rather than encoded. The source itself is split into src_cpp and src_csharp, with the public headers under include and additional pieces under modules.

Because the frames are raw pixels, an example that draws them with OpenGL is arguably the clearest demonstration of the intended pipeline: capture a frame, upload the texture, draw it.

Filtering the target list is your code, not the library's

The README's first C++ example sets up capture for all monitors, and the comment inside the configuration lambda makes the extension point obvious: add your own custom filtering there if you want to capture only some monitors. The window example does the same thing with a title search.

The full monitor setup, as the README shows it, is:

cpp
//Setup Screen Capture for all monitors
auto framgrabber =  SL::Screen_Capture::CreateCaptureConfiguration([]() {
//add your own custom filtering here if you want to capture only some monitors
    return SL::Screen_Capture::SCL_GetMonitors();
  })->onFrameChanged([&](const SL::Screen_Capture::Image& img, const SL::Screen_Capture::Monitor& monitor) {
  
  })->onNewFrame([&](const SL::Screen_Capture::Image& img, const SL::Screen_Capture::Monitor& monitor) {
  
  })->onMouseChanged([&](const SL::Screen_Capture::Image* img, const SL::Screen_Capture::MousePoint &mousepoint) {
  
  })->start_capturing();

framgrabber->SCL_SetFrameChangeInterval(std::chrono::milliseconds(100));//100 ms
framgrabber->SCL_SetMouseChangeInterval(std::chrono::milliseconds(100));//100 ms

The window case returns a filtered list instead, and the README shows the filtering being done by hand, lowercasing the search term and each window name and testing for a substring:

cpp
auto windowframgrabber =  SL::Screen_Capture::CreateCaptureConfiguration([]() {
  auto windows = SL::Screen_Capture::SCL_GetWindows();
  std::string srchterm = "cmake";
  // convert to lower case for easier comparisons
  std::transform(srchterm.begin(), srchterm.end(), srchterm.begin(), [](char c) { return std::tolower(c, std::locale());});
  decltype(windows) filtereditems;
  for(auto& a : windows) {
    std::string name = a.Name;
    std::transform(name.begin(), name.end(), name.begin(), [](char c) {return std::tolower(c, std::locale()); });
    if(name.find(srchterm) != std::string::npos) {
      filtereditems.push_back(a);
    }
  }
  return filtereditems;
  })->onFrameChanged([&](const SL::Screen_Capture::Image& img, const SL::Screen_Capture::Window& window) {

Note what the library does not do. There is no built in title matching, no regex and no per window capture rate, so anything finer grained than the target list you build is your code.

For the C# binding the same pattern applies through SL.Screen_Capture.CaptureConfiguration.CreateCaptureConfiguration, with the frame and mouse callbacks attached the same way and SCL_SetFrameChangeInterval and SCL_SetMouseChangeInterval called on the manager afterwards.

Master is not the release, and the tags are the artefact

The README opens with a warning that shapes how you should consume this project: master is where development happens and should not be considered stable, and tags are what you should use for stable releases. That is unusual candour in a project README, and it means the branch you clone by default is not the branch you should ship from.

The release tags follow a build number scheme rather than semantic versioning. The recent ones are 17.1.2745 from 2026-05-04, 17.1.2539 from 2025-03-27 and 17.1.1369 from 2023-08-14, and the last push to the repository was on 2026-05-04, the same day as the newest tag. There is a releasenotes.txt file in the root, which is where the changelog for each build is kept.

The gap between the 2023 tag and the 2025 one is worth noting if you are integrating this into a long lived product, because a build number that jumps by hundreds between tags tells you the project is versioned per internal build, and that you should expect to read the release notes before moving to a new number.

The licence is MIT, which means you can vendor the source into a proprietary product without copyleft obligations, keeping only the notice. The upgrade cost of this library is therefore a source integration and a recompile rather than a package upgrade, and with a per build numbering scheme, testing a new tag against your own capture pipeline is a real task.

Editorial conclusion

Adopt screen_capture_lite when you are writing C or C++ on Windows, macOS or Linux and need screen, window or cursor content with a per target callback model, and you want to avoid maintaining three platform backends yourself. Do not adopt it if you need a stable tagged release, because the README states that master is where development happens and should not be considered stable, or if you need encoded output, since the frames arrive as raw pixels. Verify first that the tagged build you pin matches the platform you deploy on, that the Linux development packages named in the README are installed, and that your callbacks copy the image data out before returning, because the library owns the buffer it hands you.

Frequently asked questions

What does screen_capture_lite capture?

It captures monitors, windows and mouse activity on Windows 7 and up, macOS and Linux, delivering raw BGRA frames at 32 bits per pixel. Frames arrive through the onNewFrame, onFrameChanged and onMouseChanged callbacks, and the alpha channel is only meaningful in the mouse callback.

What does screen_capture_lite need on Linux?

The README lists the Linux development packages as libxtst-dev, libxinerama-dev, libx11-dev and libxfixes-dev, installed with apt-get, and describes them as the only external dependencies of the library.

Do I have to define every callback in screen_capture_lite?

No, and the README warns against it. Only define the callbacks you are interested in, because defining onMouseChanged makes the library assume you want mouse information and do the extra work of monitoring for it.

Can I keep the captured image after the callback returns?

Not without copying it. The README states that the library owns all image data, so if you want to use an image for your own purpose after the callback has completed you must copy the data out. It also notes that each monitor or window runs in its own thread, with no internal synchronization between them.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. smasherprog/screen_capture_lite on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/smasherprog-screen-capture-lite.svg)](https://hysenlabs.com/projects/smasherprog-screen-capture-lite)

Was this useful?