Gtk2, Gdk2 memory usage gets bigger and bigger every frame update - memory-management

I have been trying to make a program that takses the active window, and displays it in its window.
I have successfully exceeded my goal. But the problem is, it uses a lot of ram, and it keeps using more every frame update(20fps).
Here is the source code:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
int funcfinished = 1;
GtkWidget *window;
GdkPixbuf *fupdate_pixbuf;
GtkStyle *fupdate_style;
GdkPixmap *fupdate_background;
gint fupdate_xorig;
gint fupdate_yorig;
gint fupdate_width;
gint fupdate_height;
GdkPixbuf *fupdate_screenshot;
GdkWindow *fupdate_window;
gboolean frameupdate()
{
if(funcfinished == 1)
{
/*********[FuncFinish]*********/
funcfinished = 0;
fupdate_pixbuf = NULL;
fupdate_style = NULL;
fupdate_background = NULL;
fupdate_screenshot = NULL;
fupdate_window = NULL;
fupdate_xorig = 0;
fupdate_yorig = 0;
fupdate_width = 0;
fupdate_height = 0;
/*********[Func]*********/
fupdate_window = gdk_screen_get_active_window(gdk_screen_get_default());
gdk_drawable_get_size(fupdate_window, &fupdate_width, &fupdate_height);
fupdate_pixbuf = gdk_pixbuf_get_from_drawable(NULL, fupdate_window, NULL, 0, 0, 0, 0, fupdate_width, fupdate_height);
gdk_pixbuf_render_pixmap_and_mask(fupdate_pixbuf, &fupdate_background, NULL, 0);
fupdate_style = gtk_style_new();
fupdate_style->bg_pixmap[0] = fupdate_background;
gtk_widget_set_style(GTK_WIDGET(window), GTK_STYLE(fupdate_style));
/*********[FuncFinish]*********/
fupdate_pixbuf = NULL;
fupdate_style = NULL;
fupdate_background = NULL;
fupdate_screenshot = NULL;
fupdate_window = NULL;
fupdate_xorig = 0;
fupdate_yorig = 0;
fupdate_width = 0;
fupdate_height = 0;
funcfinished = 1;
}
else
{
printf("Skipped 1 frame update");
}
return TRUE;
}
int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Hay Day Autobot");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
g_signal_connect(window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
g_timeout_add(50, frameupdate, 0);
gtk_widget_show(window);
gtk_main();
return 0;
}
I also made a video of it in action, showing off the problem:
https://www.youtube.com/watch?v=GNCwNetLLBM

You are not releasing the memory that you create during the frame update function. For each function that you use there, you should look it up in the documentation and see what it says under "return value".
For example, gdk_screen_get_active_window() lists its return value as "transfer full". That means that "full" ownership of the return value is "transferred" to you when you call that function; ownership means that you are responsible for freeing the memory. Usually the documentation will also say how to do that. In this case you can read
The returned window should be unrefed using g_object_unref() when no longer needed.
On the other hand, gdk_screen_get_default() is "transfer none", so you don't need to do anything there.

Related

How do I connect the Graphics Capture API to IMFSinkWriter

I am attempting to write some code that connects the Windows::Graphics::Capture API to IMFSinkWriter in order to capture the desktop to an MP4 file. I find that the IMFSinkWriter WriteSample function always returns 0x80070057 and I'm trying to understand why. I suspect there is a somewhat obvious mistake as I am not extremely familiar with COM, WinRT, DirectX, etc. Any ideas?
#include <iostream>
#include <Windows.h>
// XXX workaround bug in platform headers where this has a circular declaration
#include "winrt/base.h"
namespace winrt::impl
{
template <typename Async>
auto wait_for(Async const& async, Windows::Foundation::TimeSpan const& timeout);
}
// XXX
#include <dxgi.h>
#include <inspectable.h>
#include <dxgi1_2.h>
#include <d3d11.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <codecapi.h>
#include <strmif.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.Graphics.Capture.h>
#include <windows.graphics.capture.interop.h>
#include <windows.graphics.directx.direct3d11.interop.h>
#pragma comment(lib, "Mfuuid.lib")
#pragma comment(lib, "Mfplat.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "Mf.lib")
winrt::com_ptr<IMFSinkWriter> sinkWriter;
std::chrono::steady_clock::time_point firstFrameTime;
std::chrono::steady_clock::time_point lastFrameTime;
bool recordedFirstFrame = false;
void OnFrameArrived(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender, winrt::Windows::Foundation::IInspectable const &) {
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame frame = sender.TryGetNextFrame();
std::chrono::steady_clock::time_point frameTime = std::chrono::steady_clock::now();
LONGLONG duration = 0;
LONGLONG frametime100ns;
if (!recordedFirstFrame) {
recordedFirstFrame = true;
firstFrameTime = frameTime;
frametime100ns = 0;
}
else {
frametime100ns = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - firstFrameTime).count() / 100;
duration = std::chrono::duration_cast<std::chrono::milliseconds>(frameTime - lastFrameTime).count();
}
auto surface = frame.Surface();
auto access = surface.as<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>();
winrt::com_ptr<ID3D11Texture2D> texture;
winrt::check_hresult(access->GetInterface(winrt::guid_of<ID3D11Texture2D>(), texture.put_void()));
IMFMediaBuffer* buffer;
MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), texture.get(), 0, FALSE, &buffer);
IMFSample *sample;
winrt::check_hresult(MFCreateSample(&sample));
HRESULT hr = sample->AddBuffer(buffer);
printf("add buffer! %x\n", hr);
hr = sample->SetSampleTime(frametime100ns);
printf("set sample time (%lld) %d\n", frametime100ns, hr);
hr = sample->SetSampleDuration(duration);
printf("set sample duration (%lld) %d\n", duration, hr);
hr = sinkWriter->WriteSample(0 /* video stream index */, sample);
printf("wrote sample %x\n", hr);
lastFrameTime = frameTime;
}
int main()
{
winrt::init_apartment(winrt::apartment_type::multi_threaded);
winrt::check_hresult(MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET));
// get a list of monitor handles
std::vector<HMONITOR> monitors;
EnumDisplayMonitors(
nullptr, nullptr,
[](HMONITOR hmon, HDC, LPRECT, LPARAM lparam) {
auto& monitors = *reinterpret_cast<std::vector<HMONITOR>*>(lparam);
monitors.push_back(hmon);
return TRUE;
},
reinterpret_cast<LPARAM>(&monitors)
);
//get GraphicsCaptureItem for first monitor
auto interop_factory = winrt::get_activation_factory<winrt::Windows::Graphics::Capture::GraphicsCaptureItem, IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = { nullptr };
winrt::check_hresult(
interop_factory->CreateForMonitor(
monitors[0],
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
winrt::put_abi(item)
)
);
// Create Direct 3D Device
winrt::com_ptr<ID3D11Device> d3dDevice;
winrt::check_hresult(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
nullptr, 0, D3D11_SDK_VERSION, d3dDevice.put(), nullptr, nullptr));
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice device;
const auto dxgiDevice = d3dDevice.as<IDXGIDevice>();
{
winrt::com_ptr<::IInspectable> inspectable;
winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.get(), inspectable.put()));
device = inspectable.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
}
auto idxgiDevice2 = dxgiDevice.as<IDXGIDevice2>();
winrt::com_ptr<IDXGIAdapter> adapter;
winrt::check_hresult(idxgiDevice2->GetParent(winrt::guid_of<IDXGIAdapter>(), adapter.put_void()));
winrt::com_ptr<IDXGIFactory2> factory;
winrt::check_hresult(adapter->GetParent(winrt::guid_of<IDXGIFactory2>(), factory.put_void()));
ID3D11DeviceContext* d3dContext = nullptr;
d3dDevice->GetImmediateContext(&d3dContext);
// setup swap chain
DXGI_SWAP_CHAIN_DESC1 desc = {};
desc.Width = static_cast<uint32_t>(item.Size().Width);
desc.Height = static_cast<uint32_t>(item.Size().Height);
desc.Format = static_cast<DXGI_FORMAT>(winrt::Windows::Graphics::DirectX::DirectXPixelFormat::R16G16B16A16Float);
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferCount = 2;
desc.Scaling = DXGI_SCALING_STRETCH;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
winrt::com_ptr<IDXGISwapChain1> swapchain;
winrt::check_hresult(factory->CreateSwapChainForComposition(d3dDevice.get(), &desc, nullptr, swapchain.put()));
auto framepool = winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::CreateFreeThreaded(device, winrt::Windows::Graphics::DirectX::DirectXPixelFormat::R16G16B16A16Float, 2, item.Size());
auto session = framepool.CreateCaptureSession(item);
framepool.FrameArrived(OnFrameArrived);
//Setup MF output stream
winrt::com_ptr<IMFDXGIDeviceManager> devManager;
UINT resetToken;
winrt::check_hresult(MFCreateDXGIDeviceManager(&resetToken, devManager.put()));
winrt::check_hresult(devManager->ResetDevice(d3dDevice.get(), resetToken));
winrt::com_ptr<IMFByteStream> outputStream;
winrt::check_hresult(MFCreateFile(MF_ACCESSMODE_READWRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, L"C:\\test.mp4", outputStream.put()));
//configure MF output media type
winrt::com_ptr<IMFMediaType> videoMediaType;
//winrt::com_ptr<IMFMediaType> audioMediaType;
//for video
winrt::check_hresult(MFCreateMediaType(videoMediaType.put()));
winrt::check_hresult(videoMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
winrt::check_hresult(videoMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264));
winrt::check_hresult(videoMediaType->SetUINT32(MF_MT_AVG_BITRATE, 2000000));
winrt::check_hresult(videoMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive));
winrt::check_hresult(videoMediaType->SetUINT32(MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_Main));
winrt::check_hresult(videoMediaType->SetUINT32(MF_MT_YUV_MATRIX, MFVideoTransferMatrix_BT601));
winrt::check_hresult(MFSetAttributeSize(videoMediaType.get(), MF_MT_FRAME_SIZE, item.Size().Width, item.Size().Height));
winrt::check_hresult(MFSetAttributeRatio(videoMediaType.get(), MF_MT_FRAME_RATE, 30, 1));
winrt::check_hresult(MFSetAttributeRatio(videoMediaType.get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1));
//Creates a streaming writer
winrt::com_ptr<IMFMediaSink> mp4StreamSink;
winrt::check_hresult(MFCreateMPEG4MediaSink(outputStream.get(), videoMediaType.get(), NULL, mp4StreamSink.put()));
//setup MF Input stream
winrt::com_ptr<IMFMediaType> inputVideoMediaType;
HRESULT hr = S_OK;
GUID majortype = { 0 };
MFRatio par = { 0 };
hr = videoMediaType->GetMajorType(&majortype);
if (majortype != MFMediaType_Video)
{
throw new winrt::hresult_invalid_argument();
}
// Create a new media type and copy over all of the items.
// This ensures that extended color information is retained.
winrt::check_hresult(MFCreateMediaType(inputVideoMediaType.put()));
winrt::check_hresult(videoMediaType->CopyAllItems(inputVideoMediaType.get()));
// Set the subtype.
winrt::check_hresult(inputVideoMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32));
// Uncompressed means all samples are independent.
winrt::check_hresult(inputVideoMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE));
// Fix up PAR if not set on the original type.
hr = MFGetAttributeRatio(
inputVideoMediaType.get(),
MF_MT_PIXEL_ASPECT_RATIO,
(UINT32*)&par.Numerator,
(UINT32*)&par.Denominator
);
// Default to square pixels.
if (FAILED(hr))
{
winrt::check_hresult(MFSetAttributeRatio(
inputVideoMediaType.get(),
MF_MT_PIXEL_ASPECT_RATIO,
1, 1
));
}
winrt::check_hresult(MFSetAttributeSize(inputVideoMediaType.get(), MF_MT_FRAME_SIZE, item.Size().Width, item.Size().Height));
inputVideoMediaType->SetUINT32(MF_MT_VIDEO_ROTATION, MFVideoRotationFormat_0); //XXX where do we get the rotation from?
winrt::com_ptr<IMFAttributes> attributes;
winrt::check_hresult(MFCreateAttributes(attributes.put(), 6));
winrt::check_hresult(attributes->SetGUID(MF_TRANSCODE_CONTAINERTYPE, MFTranscodeContainerType_MPEG4));
winrt::check_hresult(attributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, 1));
winrt::check_hresult(attributes->SetUINT32(MF_MPEG4SINK_MOOV_BEFORE_MDAT, 1));
winrt::check_hresult(attributes->SetUINT32(MF_LOW_LATENCY, FALSE)); ///XXX should we?
winrt::check_hresult(attributes->SetUINT32(MF_SINK_WRITER_DISABLE_THROTTLING, FALSE)); //XX shuold we?
// Add device manager to attributes. This enables hardware encoding.
winrt::check_hresult(attributes->SetUnknown(MF_SINK_WRITER_D3D_MANAGER, devManager.get()));
//winrt::com_ptr<IMFSinkWriter> sinkWriter;
winrt::check_hresult(MFCreateSinkWriterFromMediaSink(mp4StreamSink.get(), attributes.get(), sinkWriter.put()));
sinkWriter->SetInputMediaType(0, inputVideoMediaType.get(), nullptr);
winrt::com_ptr<ICodecAPI> encoder;
sinkWriter->GetServiceForStream(0 /* video stream index */, GUID_NULL, IID_PPV_ARGS(encoder.put()));
VARIANT var;
VariantInit(&var);
var.vt = VT_UI4;
var.ulVal = eAVEncCommonRateControlMode_Quality;
winrt::check_hresult(encoder->SetValue(&CODECAPI_AVEncCommonRateControlMode, &var));
var.ulVal = 70;
winrt::check_hresult(encoder->SetValue(&CODECAPI_AVEncCommonQuality, &var));
winrt::check_hresult(sinkWriter->BeginWriting());
session.StartCapture();
std::cout << "Hello World!\n";
Sleep(1000);
session.Close();
sinkWriter->Flush(0);
sinkWriter->Finalize();
}
I was able to track down the problem. The above code had two issues:
Need to call SetCurrentLength() on the IMFMediaBuffer object. It seems silly since the way to get the length is to get the IMF2DBuffer interface from the IMFMediaBuffer object and call GetContiguousLength(), but it works.
Taking the texture straight from the OnFrameArrived() callback and passing it into the IMF sink is also wrong. This will exhaust the framepool (which is declared as having 2 frames) and hang the encoder. One possible solution is to copy the data out into a new texture before passing it to the encoder.

Taking screenshot of window (handle) in C++

I'm trying to take a screenshot of a particular window (HWND) on Windows using C++. The following code works on Notepad but not on another specific process. Instead, the code returns a completely different screenshot for the other process:
#include <Windows.h>
HBITMAP dump_client_window(const HWND window_handle)
{
RECT window_handle_rectangle;
GetClientRect(window_handle, &window_handle_rectangle);
const HDC hdc_screen = GetDC(nullptr);
const HDC hdc = CreateCompatibleDC(hdc_screen);
const auto cx = window_handle_rectangle.right - window_handle_rectangle.left;
const auto cy = window_handle_rectangle.bottom - window_handle_rectangle.top;
const HBITMAP bitmap = CreateCompatibleBitmap(hdc_screen, cx, cy);
SelectObject(hdc, bitmap);
const auto old_bitmap = SelectObject(hdc, bitmap);
PrintWindow(window_handle, hdc, PW_CLIENTONLY);
// Cleanup
SelectObject(hdc, old_bitmap);
DeleteDC(hdc);
ReleaseDC(nullptr, hdc_screen);
return bitmap;
}
What could be the reason for it? If I use DirectX11 for taking the screenshot of the window, it works correctly for both processes:
#include <dxgi.h>
#include <inspectable.h>
#include <dxgi1_2.h>
#include <d3d11.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.Graphics.Capture.h>
#include <Windows.Graphics.Capture.Interop.h>
#include <windows.graphics.directx.direct3d11.interop.h>
#include <roerrorapi.h>
#include <ShlObj_core.h>
#include <dwmapi.h>
#include <filesystem>
#include "ImageFormatConversion.hpp"
#pragma comment(lib, "Dwmapi.lib")
#pragma comment(lib, "windowsapp.lib")
void capture_window(HWND window_handle, const std::wstring& output_file_path)
{
// Init COM
init_apartment(winrt::apartment_type::multi_threaded);
// Create Direct 3D Device
winrt::com_ptr<ID3D11Device> d3d_device;
winrt::check_hresult(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
nullptr, 0, D3D11_SDK_VERSION, d3d_device.put(), nullptr, nullptr));
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice device;
const auto dxgiDevice = d3d_device.as<IDXGIDevice>();
{
winrt::com_ptr<IInspectable> inspectable;
winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.get(), inspectable.put()));
device = inspectable.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
}
auto idxgi_device2 = dxgiDevice.as<IDXGIDevice2>();
winrt::com_ptr<IDXGIAdapter> adapter;
winrt::check_hresult(idxgi_device2->GetParent(winrt::guid_of<IDXGIAdapter>(), adapter.put_void()));
winrt::com_ptr<IDXGIFactory2> factory;
winrt::check_hresult(adapter->GetParent(winrt::guid_of<IDXGIFactory2>(), factory.put_void()));
ID3D11DeviceContext* d3d_context = nullptr;
d3d_device->GetImmediateContext(&d3d_context);
RECT rect{};
DwmGetWindowAttribute(window_handle, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(RECT));
const auto size = winrt::Windows::Graphics::SizeInt32{ rect.right - rect.left, rect.bottom - rect.top };
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool m_frame_pool =
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::Create(
device,
winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized,
2,
size);
const auto activation_factory = winrt::get_activation_factory<
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem capture_item = { nullptr };
interop_factory->CreateForWindow(window_handle, winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
winrt::put_abi(capture_item));
auto is_frame_arrived = false;
winrt::com_ptr<ID3D11Texture2D> texture;
const auto session = m_frame_pool.CreateCaptureSession(capture_item);
m_frame_pool.FrameArrived([&](auto& frame_pool, auto&)
{
if (is_frame_arrived)
{
return;
}
auto frame = frame_pool.TryGetNextFrame();
struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
IDirect3DDxgiInterfaceAccess : ::IUnknown
{
virtual HRESULT __stdcall GetInterface(GUID const& id, void** object) = 0;
};
auto access = frame.Surface().as<IDirect3DDxgiInterfaceAccess>();
access->GetInterface(winrt::guid_of<ID3D11Texture2D>(), texture.put_void());
is_frame_arrived = true;
return;
});
session.StartCapture();
// Message pump
MSG message;
while (!is_frame_arrived)
{
if (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE) > 0)
{
DispatchMessage(&message);
}
}
session.Close();
D3D11_TEXTURE2D_DESC captured_texture_desc;
texture->GetDesc(&captured_texture_desc);
captured_texture_desc.Usage = D3D11_USAGE_STAGING;
captured_texture_desc.BindFlags = 0;
captured_texture_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
captured_texture_desc.MiscFlags = 0;
winrt::com_ptr<ID3D11Texture2D> user_texture = nullptr;
winrt::check_hresult(d3d_device->CreateTexture2D(&captured_texture_desc, nullptr, user_texture.put()));
d3d_context->CopyResource(user_texture.get(), texture.get());
D3D11_MAPPED_SUBRESOURCE resource;
winrt::check_hresult(d3d_context->Map(user_texture.get(), NULL, D3D11_MAP_READ, 0, &resource));
BITMAPINFO l_bmp_info;
// BMP 32 bpp
ZeroMemory(&l_bmp_info, sizeof(BITMAPINFO));
l_bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
l_bmp_info.bmiHeader.biBitCount = 32;
l_bmp_info.bmiHeader.biCompression = BI_RGB;
l_bmp_info.bmiHeader.biWidth = captured_texture_desc.Width;
l_bmp_info.bmiHeader.biHeight = captured_texture_desc.Height;
l_bmp_info.bmiHeader.biPlanes = 1;
l_bmp_info.bmiHeader.biSizeImage = captured_texture_desc.Width * captured_texture_desc.Height * 4;
std::unique_ptr<BYTE> p_buf(new BYTE[l_bmp_info.bmiHeader.biSizeImage]);
UINT l_bmp_row_pitch = captured_texture_desc.Width * 4;
auto sptr = static_cast<BYTE*>(resource.pData);
auto dptr = p_buf.get() + l_bmp_info.bmiHeader.biSizeImage - l_bmp_row_pitch;
UINT l_row_pitch = std::min<UINT>(l_bmp_row_pitch, resource.RowPitch);
for (size_t h = 0; h < captured_texture_desc.Height; ++h)
{
memcpy_s(dptr, l_bmp_row_pitch, sptr, l_row_pitch);
sptr += resource.RowPitch;
dptr -= l_bmp_row_pitch;
}
// Save bitmap buffer into the file
WCHAR l_my_doc_path[MAX_PATH];
winrt::check_hresult(SHGetFolderPathW(nullptr, CSIDL_PERSONAL, nullptr, SHGFP_TYPE_CURRENT, l_my_doc_path));
FILE* lfile = nullptr;
if (auto lerr = _wfopen_s(&lfile, output_file_path.c_str(), L"wb"); lerr != 0)
{
return;
}
if (lfile != nullptr)
{
BITMAPFILEHEADER bmp_file_header;
bmp_file_header.bfReserved1 = 0;
bmp_file_header.bfReserved2 = 0;
bmp_file_header.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + l_bmp_info.bmiHeader.biSizeImage;
bmp_file_header.bfType = 'MB';
bmp_file_header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
fwrite(&bmp_file_header, sizeof(BITMAPFILEHEADER), 1, lfile);
fwrite(&l_bmp_info.bmiHeader, sizeof(BITMAPINFOHEADER), 1, lfile);
fwrite(p_buf.get(), l_bmp_info.bmiHeader.biSizeImage, 1, lfile);
fclose(lfile);
convert_image_encoding(output_file_path, L"png");
}
}
Why is the DirectX11 code so complex/long and slow (about 800ms - 1s per call including cold start initialization)? Also, the latter version causes blinking borders around the captured window which I might want to get rid of. I also seem to have to take the more inefficient route of storing the BMP image to the disk and then loading it back in order to convert it to PNG and then storing it again to produce the final result on the disk which I like to have.
Any suggestions or help with any of these things are welcome, especially why the first screenshot capture code can yield unexpected images depending on the window being captured. Other than that, I like the first version for its speed, brevity and simplicity.

freedesktop XEmbed systray client code in C, Xlib

I've been trying to implement a systray icon using straight C and Xlib, going along with the freedesktop specification [0]. I can't seem to get my Window to embed into my desktop manager's systray[1], while other apps seem to be able to do it. I am not sure how to go forward debugging this, but I've provided minimal sample code below.
I haven't been able to find any sample code using straight Xlib and C, and all the suggestions I've seen have been with regard to some framework like Gtk/Qt/Mono/whatever, but I want to understand what is supposed to be happening here as per the spec, and what I'm doing wrong.
#include <X11/Xutil.h>
#include <string.h>
#define MIN(A, B) ((A) < (B) ? (A) : (B))
/* --------- XEMBED and systray stuff */
#define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
static int trapped_error_code = 0;
static int (*old_error_handler) (Display *, XErrorEvent *);
static int
error_handler(Display *display, XErrorEvent *error) {
trapped_error_code = error->error_code;
return 0;
}
void
trap_errors(void) {
trapped_error_code = 0;
old_error_handler = XSetErrorHandler(error_handler);
}
int
untrap_errors(void) {
XSetErrorHandler(old_error_handler);
return trapped_error_code;
}
void
send_systray_message(Display* dpy, Window w, long message, long data1, long data2, long data3) {
XEvent ev;
memset(&ev, 0, sizeof(ev));
ev.xclient.type = ClientMessage;
ev.xclient.window = w;
ev.xclient.message_type = XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False );
ev.xclient.format = 32;
ev.xclient.data.l[0] = CurrentTime;
ev.xclient.data.l[1] = message;
ev.xclient.data.l[2] = data1;
ev.xclient.data.l[3] = data2;
ev.xclient.data.l[4] = data3;
trap_errors();
XSendEvent(dpy, w, False, NoEventMask, &ev);
XSync(dpy, False);
if (untrap_errors()) {
/* Handle errors */
}
}
/* ------------ Regular X stuff */
int
main(int argc, char **argv) {
int width, height;
XWindowAttributes wa;
XEvent ev;
Display *dpy;
int screen;
Window root, win;
/* init */
if (!(dpy=XOpenDisplay(NULL)))
return 1;
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
if(!XGetWindowAttributes(dpy, root, &wa))
return 1;
width = height = MIN(wa.width, wa.height);
/* create window */
win = XCreateSimpleWindow(dpy, root, 0, 0, width, height, 0, 0, 0xFFFF9900);
send_systray_message(dpy, win, SYSTEM_TRAY_REQUEST_DOCK, win, 0, 0);
XMapWindow(dpy, win);
XSync(dpy, False);
/* run */
while(1) {
while(XPending(dpy)) {
XNextEvent(dpy, &ev); /* just waiting until we error because window closed */
}
}
}
Any help would be greatly appreciated. I think this problem is language-agnostic, and more to do with me misunderstanding the protocols, so answers in any language are acceptable, as long as they help me iron out this XEvent stuff.
[0] https://specifications.freedesktop.org/systemtray-spec/systemtray-spec-0.2.html
[1] I'm using dwm with the systray patch http://dwm.suckless.org/patches/systray
You are sending the message to a wrong window. The documentation isn't really helpful, it makes no sense whatsoever to send a tray embed message to your own window!. You need to send it to the tray window.
Here's a a fixed send_systray_message
void
send_systray_message(Display* dpy, long message, long data1, long data2, long data3) {
XEvent ev;
Atom selection_atom = XInternAtom (dpy,"_NET_SYSTEM_TRAY_S0",False);
Window tray = XGetSelectionOwner (dpy,selection_atom);
if ( tray != None)
XSelectInput (dpy,tray,StructureNotifyMask);
memset(&ev, 0, sizeof(ev));
ev.xclient.type = ClientMessage;
ev.xclient.window = tray;
ev.xclient.message_type = XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False );
ev.xclient.format = 32;
ev.xclient.data.l[0] = CurrentTime;
ev.xclient.data.l[1] = message;
ev.xclient.data.l[2] = data1; // <--- your window is only here
ev.xclient.data.l[3] = data2;
ev.xclient.data.l[4] = data3;
trap_errors();
XSendEvent(dpy, tray, False, NoEventMask, &ev);
XSync(dpy, False);
usleep(10000);
if (untrap_errors()) {
/* Handle errors */
}
}
and a call to it
send_systray_message(dpy, SYSTEM_TRAY_REQUEST_DOCK, win, 0, 0); // pass win only once
Credits: http://distro.ibiblio.org/vectorlinux/Uelsk8s/GAMBAS/gambas-svn/gambas2/gb.gtk/src/gtrayicon.cpp

Programatically take screen shots and save as PNG

I've been using the following code for quite a while:
#include <fstream>
#include <gdiplus.h>
#include <windows.h>
#include <iostream>
using namespace std;
void CaptureScreen(const char* filename)
{
HDC hScreenDC = GetDC(0);
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int upper_left_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
int upper_left_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
int bitmap_dx = GetSystemMetrics(SM_CXVIRTUALSCREEN ) * 1.25f;
int bitmap_dy = GetSystemMetrics(SM_CYVIRTUALSCREEN ) * 1.25f;
// create file
ofstream file(filename, ios::binary);
if(!file) return;
// save bitmap file headers
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
fileHeader.bfType = 0x4d42;
fileHeader.bfSize = 0;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
infoHeader.biSize = sizeof(infoHeader);
infoHeader.biWidth = bitmap_dx;
infoHeader.biHeight = -bitmap_dy;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 16;
infoHeader.biCompression = BI_RGB;
infoHeader.biSizeImage = 0;
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0;
file.write((char*)&fileHeader, sizeof(fileHeader));
file.write((char*)&infoHeader, sizeof(infoHeader));
// dibsection information
BITMAPINFO info;
info.bmiHeader = infoHeader;
// ------------------
// THE IMPORTANT CODE
// ------------------
// create a dibsection and blit the window contents to the bitmap
BYTE* memory = 0;
HBITMAP bitmap = CreateDIBSection(hScreenDC, &info, DIB_RGB_COLORS, (void**)&memory, 0, 0);
SelectObject(hMemoryDC, bitmap);
BitBlt(hMemoryDC, 0, 0, bitmap_dx, bitmap_dy, hScreenDC, upper_left_x, upper_left_y, SRCCOPY);
DeleteDC(hMemoryDC);
ReleaseDC(NULL, hScreenDC);
// save dibsection data
int bytes = (((16*bitmap_dx + 31) & (~31))/8)*bitmap_dy;
file.write((const char *)memory, bytes);
DeleteObject(bitmap);
}
int main()
{
CaptureScreen("ok.jpg");
return 0;
}
But it seems to generate a too large BMP file because the bitmaps are saved uncompressed.
I'm looking for a way to capture a screen shot and save it to a buffer in PNG format, send it over a TCP connection and save it as a PNG file there.
I believe it has something to do with assigning BI_PNG to infoHeader.biCompression and a different calculation of bytes but I can't figure exactly what.
http://lodev.org/lodepng/
#include <fstream>
#include <gdiplus.h>
#include <windows.h>
#include <iostream>
#include "lodepng.h"
const int bits_per_pixel = 24;
using namespace std;
void CaptureScreen(const char* filename)
{
HDC hScreenDC = GetDC(0);
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int upper_left_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
int upper_left_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
int bitmap_dx = GetSystemMetrics(SM_CXVIRTUALSCREEN ) * 1.25f;
int bitmap_dy = GetSystemMetrics(SM_CYVIRTUALSCREEN ) * 1.25f;
// create file
ofstream file(filename, ios::binary);
if(!file) return;
// save bitmap file headers
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
fileHeader.bfType = 0x4d42;
fileHeader.bfSize = 0;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
infoHeader.biSize = sizeof(infoHeader);
infoHeader.biWidth = bitmap_dx;
infoHeader.biHeight = -bitmap_dy;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = bits_per_pixel;
infoHeader.biCompression = BI_RGB;
infoHeader.biSizeImage = 0;
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0;
file.write((char*)&fileHeader, sizeof(fileHeader));
file.write((char*)&infoHeader, sizeof(infoHeader));
// dibsection information
BITMAPINFO info;
info.bmiHeader = infoHeader;
// ------------------
// THE IMPORTANT CODE
// ------------------
// create a dibsection and blit the window contents to the bitmap
BYTE* memory = 0;
HBITMAP bitmap = CreateDIBSection(hScreenDC, &info, DIB_RGB_COLORS, (void**)&memory, 0, 0);
SelectObject(hMemoryDC, bitmap);
BitBlt(hMemoryDC, 0, 0, bitmap_dx, bitmap_dy, hScreenDC, upper_left_x, upper_left_y, SRCCOPY);
DeleteDC(hMemoryDC);
ReleaseDC(NULL, hScreenDC);
// save dibsection data
int bytes = (((bits_per_pixel*bitmap_dx + 31) & (~31))/8)*bitmap_dy;
file.write((const char *)memory, bytes);
unsigned char *out_buffer;
size_t out_buffer_len;
unsigned error;
if ( bits_per_pixel == 24 )
{
// convert memory from bgr format to rgb
for ( unsigned i = 0; i< bytes-2; i+=3)
{
int tmp = memory[i+2];
memory[i+2] = memory[i];
memory[i] = tmp;
}
error = lodepng_encode24(&out_buffer,
&out_buffer_len,
memory,
bitmap_dx,
bitmap_dy);
}
if ( bits_per_pixel == 32 )
{
// convert memory from bgr format to rgb
for ( unsigned i = 0; i< bytes-3; i+=4)
{
int tmp = memory[i+2];
memory[i+2] = memory[i];
memory[i] = tmp;
}
error = lodepng_encode32(&out_buffer,
&out_buffer_len,
memory,
bitmap_dx,
bitmap_dy);
}
if ( error )
{
std::cout << "error: " << error << '\n';
return;
}
lodepng_save_file(out_buffer, out_buffer_len, "stam.png");
// free(out);
DeleteObject(bitmap);
}
int main()
{
CaptureScreen("ok.jpg");
return 0;
}

SDL 2.0 Issue - Trying to load image, but only get a white box

Okay, so I am trying to load a window and display a couple of images on it using a function. The window loads and my errors don't display for a failure to load the image, however the window just stays white. Any ideas why that might be? This is my code below.
#include "stdafx.h"
#include "SDL.h"
#include <iostream>
#include <string>
using namespace std;
const int Window_Width = 640;
const int Window_Height = 480;
SDL_Window *window = NULL;
SDL_Renderer *render = NULL;
SDL_Texture* loadImage(string imagename) //function that loads the image, useful for handling multiple image imports
{
SDL_Surface* loadedImage = NULL;
SDL_Texture* texture = NULL;
loadedImage = SDL_LoadBMP(imagename.c_str()); //loads the image with the passed file name
if (loadedImage == NULL) //checks for any errors loading the image
{
cout<<"The image failed to load.."<<endl;
}
texture = SDL_CreateTextureFromSurface(render, loadedImage);
SDL_FreeSurface(loadedImage);
return texture;
}
int main(int argc, char** argv)
{
if (SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
cout << SDL_GetError() << endl;
return 1;
}
window = SDL_CreateWindow("Frogger", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Window_Width, Window_Height, SDL_WINDOW_SHOWN);
//creates a window in the centre of the screen, it uses const int's to define the size of the window
if (window == NULL)
{
cout << SDL_GetError()<<endl;
return 1;
}
render = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
//this renders the window
if (render == NULL)
{
cout << SDL_GetError()<<endl;
return 1;
}
//loading the images using the function
SDL_Texture* background = NULL;
SDL_Texture* frog = NULL;
background = loadImage("background.bmp");
frog = loadImage("frogger.bmp");
SDL_Delay(2000);
SDL_RenderClear(render);
SDL_RenderPresent(render);
SDL_UpdateWindowSurface(window);
//freeing the memory back up
SDL_DestroyRenderer(render);
SDL_DestroyWindow(window);
SDL_DestroyTexture(background);
SDL_DestroyTexture(frog);
SDL_Quit();
return 0;
}
You aren't rendering anything, you just load some textures and end the program.

Resources