반응형

wxWidgets로 만든 앱에서 윈도우 핸들(HWND)을 확인해 보자.

 

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
	bool OnInit() override;
};

wxIMPLEMENT_APP(MyApp);

class MyFrame : public wxFrame
{
public:
	MyFrame();

private:
	void OnHello(wxCommandEvent& event);
	void OnExit(wxCommandEvent& event);
	void OnAbout(wxCommandEvent& event);
};

enum
{
	ID_Hello = 1
};

bool MyApp::OnInit()
{
	MyFrame* frame = new MyFrame();
	frame->Show(true);
	return true;
}

MyFrame::MyFrame()
	: wxFrame(nullptr, wxID_ANY, "Hello World")
{
	wxMenu* menuFile = new wxMenu;
	menuFile->Append(ID_Hello, "&Hello...\tCtrl-H", "Help string shown in status bar for this menu item");
	menuFile->AppendSeparator();
	menuFile->Append(wxID_EXIT);

	wxMenu* menuHelp = new wxMenu;
	menuHelp->Append(wxID_ABOUT);

	wxMenuBar* menuBar = new wxMenuBar;
	menuBar->Append(menuFile, "&File");
	menuBar->Append(menuHelp, "&Help");

	SetMenuBar(menuBar);

	CreateStatusBar();
	SetStatusText("Welcome to wxWidgets!");

	wxPanel* panel = new wxPanel(this, wxID_ANY);
	wxButton* myButton = new wxButton(panel, wxID_ANY, "Press Me", wxPoint(30, 30), wxSize(200, 30));
	myButton->Bind(wxEVT_BUTTON, &MyFrame::OnHello, this);

	Bind(wxEVT_MENU, &MyFrame::OnHello, this, ID_Hello);
	Bind(wxEVT_MENU, &MyFrame::OnAbout, this, wxID_ABOUT);
	Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT);
}

void MyFrame::OnExit(wxCommandEvent& event)
{
	Close(true);
}

void MyFrame::OnAbout(wxCommandEvent& event)
{
	wxMessageBox("This is a wxWidgets Hello World example", "About Hello World", wxOK | wxICON_INFORMATION);
}

void MyFrame::OnHello(wxCommandEvent& event)
{
	// 버튼의 윈도우 핸들을 가져와서 텍스트를 변경한다.
	// 실행시간에 이벤트가 발생한 객체가 wxButton인지 확인하기 위해 dynamic_cast를 사용한다.
	wxButton* button = dynamic_cast<wxButton*>(event.GetEventObject());
	if (button)
	{
		// GetHandle()을 통해 네이티브 운영체제의 윈도우 핸들(HWND)을 가져올 수 있다.
		// 이벤트가 발생한 객체가 wxButton인 경우, 해당 버튼의 핸들을 표시한다.
		void* handle = button->GetHandle();
		//void* handle = button->GetHWND(); // GetHWND()는 wxButton의 핸들을 반환하는 메서드로, GetHandle()과 동일한 값을 반환한다.
		button->SetLabel(wxString::Format("HWND: %p", handle));
	}
	else
	{
		// 버튼이 아닌 메뉴에서 호출된 경우, 메인 윈도우(MyFrame)의 핸들을 표시한다.
		void* frameHandle = this->GetHandle();
		wxLogMessage(wxString::Format("Main Window HWND: %p", frameHandle));
	}
}

 

 

버튼을 클릭하면 버튼의 윈도우 핸들을 표시한다.

 

File - Hello... 클릭

 

메인 윈도우의 핸들을 표시한다.

 

 

이번엔 타이틀바 왼쪽의 아이콘을 바꿔보자.

 

icon.ico
0.01MB

 

MyFrame::MyFrame()
	: wxFrame(nullptr, wxID_ANY, "Hello World")
{
	// 윈도우 타이틀바 왼쪽에 표시될 아이콘 설정
	// 프로젝트(혹은 실행 파일) 경로에 "icon.ico" 파일이 존재해야 정상적으로 로드된다.
	SetIcon(wxIcon("icon.ico", wxBITMAP_TYPE_ICO));

	wxMenu* menuFile = new wxMenu;
	menuFile->Append(ID_Hello, "&Hello...\tCtrl-H", "Help string shown in status bar for this menu item");
	menuFile->AppendSeparator();
	menuFile->Append(wxID_EXIT);
    .
    .
    .

 

타이틀바 왼쪽의 아이콘이 체리로 바뀌었다.

 

이번엔 리소스 파일을 이용해 타이틀바의 아이콘뿐만 아니라 실행 파일의 아이콘도 바꿔보자.

 

 

 

 

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 영어(미국) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_KOR)
LANGUAGE 18, 1

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE  
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE  
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE  
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

#endif    // 영어(미국) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

// Windows 탐색기에서 표시할 실행 파일 메인 아이콘
MAINICON ICON "icon.ico"

// 참고: wxWidgets 기본 리소스(매니페스트 및 기본 커서 등)를 포함하려면 아래 줄을 추가하는 것이 좋다.
#include "wx/msw/wx.rc"

 

리소스 파일 아래에 위와 같이 MAINICON ICON "icon.ico" 문장을 추가한다. Windows는 프로젝트에 포함된 리소스 파일 중 가장 먼저(또는 가장 낮은 ID로) 정의된 아이콘을 실행 파일의 주 아이콘으로 사용한다. (#include "wx/msw/wx.rc"를 추가하면 wxWidgets 기본 리소스를 사용할 수 있다)

 

이 상태에서 다시 빌드하면 실행 파일의 아이콘이 아래와 같이 변경된다.

 

또, 리소스에 아이콘을 등록했으므로 타이틀바 아이콘 설정도 아래와 같이 바꿀 수 있다.

MyFrame::MyFrame()
	: wxFrame(nullptr, wxID_ANY, "Hello World")
{
	// 윈도우 타이틀바 왼쪽에 표시될 아이콘 설정
	// 프로젝트(혹은 실행 파일) 경로에 "icon.ico" 파일이 존재해야 정상적으로 로드된다.
	//SetIcon(wxIcon("icon.ico", wxBITMAP_TYPE_ICO));
	// 
	// 리소스에 아이콘을 등록했기 때문에, Source.cpp에서 파일 경로를 직접 적지 않고 리소스에 등록된 아이콘명을
	// 활용하여 타이틀바 아이콘을 설정할 수 있다. (wxIcon() 대신 매크로 wxICON() 사용)
	SetIcon(wxICON(MAINICON)); // 리소스에 등록된 아이콘을 사용하여 타이틀바 아이콘 설정("MAINICON"은 리소스에 선언한 이름)
	//
	// 리소스에 #include "wx/msw/wx.rc"를 추가했기 때문에, wxWidgets의 기본 리소스(매니페스트 및 기본 커서 등)가 포함되어 있다.
	// 따라서 wx.rc에 정의된 기본 아이콘을 사용할 수 있다. 예를 들어, wxICON(wxICON_INFORMATION)과 같이 기본 아이콘을 사용할 수 있다.
	//SetIcon(wxICON(wxICON_INFORMATION));

	wxMenu* menuFile = new wxMenu;
	menuFile->Append(ID_Hello, "&Hello...\tCtrl-H", "Help string shown in status bar for this menu item");
	menuFile->AppendSeparator();
	menuFile->Append(wxID_EXIT);
    .
    .
    .

 

반응형
Posted by J-sean
:
반응형

파티클은 아니지만 차일드 윈도우로 화면에 내리는 눈을 만들어 보자.

 

// 차일드 윈도우가 100개 정도만 되어도 엄청 느려지고 잘 안된다.
// 그러다 그 이상되면 101번째 윈도우 생성시(정확히는 렌더러 생성시) 아래와 같은 Access Violation이 발생한다.
// Exception thrown at 0x00007FF8EE239D68 (nvd3dumx.dll) in SDLTest.exe: 0xC0000005 : Access violation reading location 0x0000000000000038.
// SDL3가 문제인지, 그래픽 드라이버가 문제인지... 정확히는 모르겠지만 아무래도 그래픽 드라이버 문제같다.

#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <stdio.h>
#include <windows.h>

SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;

const int number_flakes = 10;
int screen_width;
int screen_height;

SDL_Window* subwindow[number_flakes]; // 서브 윈도우
SDL_Renderer* subrenderer[number_flakes]; // 서브 윈도우 렌더러

typedef struct position {
	float x, y;
	float vx, vy;
} s_position;
s_position subwindow_pos[number_flakes]; // 서브 윈도우 위치

// 파티클 이미지 텍스쳐와 렉트
SDL_Texture* texture[number_flakes];
SDL_FRect rect;

SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[])
{
	ShowWindow(GetConsoleWindow(), SW_HIDE);
	// 콘솔 창 숨김. Project - Property Pages - Linker - System - SubSystem - Windows로 바꿔서 컴파일해도 된다.

	SDL_SetAppMetadata("Example", "1.0", "sean");

	if (!SDL_Init(SDL_INIT_VIDEO)) {
		SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
		return SDL_APP_FAILURE;
	}

	// 부모	윈도우 생성하고 최소화
	if (!SDL_CreateWindowAndRenderer("Press ESC to Quit", 320, 32, SDL_WINDOW_MINIMIZED, &window, &renderer)) {
		SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
		return SDL_APP_FAILURE;
	}

	// 비트맵 로드
	SDL_Surface* bmpSurface = SDL_LoadBMP("snow.bmp");
	if (!bmpSurface) {
		SDL_Log("Couldn't load BMP: %s", SDL_GetError());
		return SDL_APP_FAILURE;
	}

	// 컬러키(투명) 설정
	SDL_SetSurfaceColorKey(bmpSurface, true, SDL_MapSurfaceRGB(bmpSurface, 0x00, 0x00, 0x00));
	//SDL_SetSurfaceColorKey(bmpSurface, true, SDL_MapRGB(SDL_GetPixelFormatDetails(bmpSurface->format), NULL, 0x00, 0x00, 0x00));
	rect = { 0, 0, (float)bmpSurface->w, (float)bmpSurface->h };

	// 현재 디스플레이 정보 가져오기(해상도, 주사율 등)
	const SDL_DisplayMode* displaymode = SDL_GetCurrentDisplayMode(SDL_GetPrimaryDisplay());
	screen_width = displaymode->w / 3;
	screen_height = displaymode->h / 3;

	for (int i = 0; i < number_flakes; i++) {
		subwindow_pos[i].x = (float)SDL_rand(screen_width);
		subwindow_pos[i].y = (float)SDL_rand(screen_height); // 서브 윈도우 위치 설정
		subwindow_pos[i].vx = SDL_randf() * 0.5f;
		subwindow_pos[i].vy = SDL_randf() + 0.8f;

		/*
		// 서브 윈도우 생성
		SDL_PropertiesID props = SDL_CreateProperties(); // Create Properties
		SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN, true);
		SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN, true);
		SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN, true);
		SDL_GetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, true);
		SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, rect.w);
		SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, rect.h);
		SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, subwindow_pos[i].x);
		SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, subwindow_pos[i].y);
		SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, window);

		subwindow[i] = SDL_CreateWindowWithProperties(props);
		if (!subwindow[i]) {
			SDL_Log("Couldn't create subwindow: %s", SDL_GetError());
			return SDL_APP_FAILURE;
		}

		subrenderer[i] = SDL_CreateRenderer(subwindow[i], NULL);
		if (!subrenderer[i]) {
			SDL_Log("Couldn't create subwindow renderer: %s", SDL_GetError());
			return SDL_APP_FAILURE;
		}
		*/

		// 위 주석의 코드와 동일
		if (!SDL_CreateWindowAndRenderer("Sub Window", (int)rect.w, (int)rect.h, SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_TRANSPARENT, &subwindow[i], &subrenderer[i])) {
			SDL_Log("Couldn't create subwindow and subrenderer: %s", SDL_GetError());
			return SDL_APP_FAILURE;
		}
		SDL_SetWindowParent(subwindow[i], window); // 서브 윈도우 부모 설정		
		SDL_SetWindowPosition(subwindow[i], (int)subwindow_pos[i].x, (int)subwindow_pos[i].y);

		SDL_SetRenderDrawBlendMode(subrenderer[i], SDL_BLENDMODE_BLEND);
	}

	for (int i = 0; i < number_flakes; i++) {
		texture[i] = SDL_CreateTextureFromSurface(subrenderer[i], bmpSurface);
		if (!texture[i]) {
			SDL_Log("Couldn't create texture: %s", SDL_GetError());
			return SDL_APP_FAILURE;
		}
	}

	SDL_DestroySurface(bmpSurface);

	return SDL_APP_CONTINUE;
}

SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
{
	switch (event->type) {
	case SDL_EVENT_QUIT:
		return SDL_APP_SUCCESS;
	case SDL_EVENT_KEY_DOWN:
		printf("Key pressed: %s\n", SDL_GetKeyName(event->key.key));
		if (event->key.key == SDLK_ESCAPE)
			return SDL_APP_SUCCESS;
		break;
	default:
		break;
	}

	return SDL_APP_CONTINUE;
}

SDL_AppResult SDL_AppIterate(void* appstate)
{
	SDL_Delay(1000 / 60); // 60 FPS

	SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
	SDL_RenderClear(renderer);

	SDL_RenderPresent(renderer);

	for (int i = 0; i < number_flakes; i++) {
		SDL_SetRenderDrawColor(subrenderer[i], 0, 0, 0, SDL_ALPHA_TRANSPARENT);
		SDL_RenderClear(subrenderer[i]);
		SDL_RenderTexture(subrenderer[i], texture[i], NULL, &rect);
		SDL_RenderPresent(subrenderer[i]);

		// 서브 윈도우 위치 업데이트
		subwindow_pos[i].x += subwindow_pos[i].vx; // 오른쪽으로 이동
		subwindow_pos[i].y += subwindow_pos[i].vy; // 아래로 이동

		if (subwindow_pos[i].x > screen_width) {
			subwindow_pos[i].x = 0; // 화면 왼쪽으로 다시 이동
		}
		if (subwindow_pos[i].y > screen_height) {
			subwindow_pos[i].y = 0; // 화면 위로 다시 이동
		}
		SDL_SetWindowPosition(subwindow[i], (int)subwindow_pos[i].x, (int)subwindow_pos[i].y);
	}

	return SDL_APP_CONTINUE;
}

void SDL_AppQuit(void* appstate, SDL_AppResult result)
{
	for (int i = 0; i < number_flakes; i++) {
		SDL_DestroyTexture(texture[i]);
		SDL_DestroyRenderer(subrenderer[i]);
		SDL_DestroyWindow(subwindow[i]);
	}

	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);

	SDL_Quit();
}

 

 

 

하지만 위 예처럼 눈의 수 만큼 차일드 윈도우를 생성하는건 부담스럽다.

특별한 비활성화 투명 윈도우를 생성하는 방식으로 바꿔보자.

 

#define SDL_MAIN_USE_CALLBACKS 1
#include "SDL3/SDL.h"
#include "SDL3/SDL_main.h"
#include <stdio.h>

#include <windows.h>
#include <WinUser.h>

SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;

int screen_width = GetSystemMetrics(SM_CXSCREEN);
int screen_height = GetSystemMetrics(SM_CYSCREEN);

Uint64 start;
Uint64 end;
float fps;

typedef struct {
	float x, y;
	float vx, vy;
} s_particle;

const int number_particles = 2000;
s_particle particles[number_particles];

// 파티클 이미지 텍스쳐와 렉트
SDL_Texture* texture;
SDL_FRect rect;

void spawn_particle(s_particle* pt) {
	pt->x = (SDL_randf() * 2 - 1) * screen_width * 10;
	pt->y = SDL_randf() * screen_height * -2;
	// 넓은 범위에서 눈이 생성되게 해서 실행 초기에 눈이 쏟아져 내리는걸 방지.
	pt->vx = SDL_randf() * 0.5f; // 눈은 오른쪽으로만 흩날린다.
	pt->vy = SDL_randf() + 0.8f;
}

void draw_particle(s_particle pt[]) {
	for (int i = 0; i < number_particles; i++) {
		s_particle* particle = &pt[i];
		particle->x += particle->vx;
		particle->y += particle->vy;
		// 눈이 화면 왼쪽에서도 바람에 날려 올 수 있도록 생존 범위 조정.
		if (particle->x > screen_width || particle->x < -screen_width || particle->y > screen_height)
			spawn_particle(particle);

		// 텍스쳐 그리기
		rect.x = particle->x - rect.w / 2;
		rect.y = particle->y - rect.h / 2;
		SDL_RenderTexture(renderer, texture, NULL, &rect);

		//SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
		//SDL_RenderPoint(renderer, particle->x, particle->y);
	}
}

SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[])
{
	SDL_SetAppMetadata("Example", "1.0", "sean");

	if (!SDL_Init(SDL_INIT_VIDEO)) {
		SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
		return SDL_APP_FAILURE;
	}

	SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
	// A variable controlling whether updates to the SDL screen surface should be synchronized
	// with the vertical refresh, to avoid tearing.

	// 디스플레이 스케일 정보 확인
	SDL_DisplayID display = SDL_GetPrimaryDisplay(); // Return the primary display
	SDL_Log("DisplayID: %u", display);
	float display_scale = SDL_GetDisplayContentScale(display);
	SDL_Log("Display Scale: %f", display_scale);
	SDL_Log("Original Display Size: (%d X %d)", screen_width, screen_height);

	// 스케일 적용
	screen_width = int(screen_width * display_scale);
	screen_height = int(screen_height * display_scale);

	SDL_Log("Scaled Display Size: (%d X %d)", screen_width, screen_height);

	if (!SDL_CreateWindowAndRenderer("example", screen_width, screen_height, SDL_WINDOW_TRANSPARENT | SDL_WINDOW_BORDERLESS | SDL_WINDOW_FULLSCREEN, &window, &renderer)) {
		SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
		return SDL_APP_FAILURE;
	}

	// 투명 배경 설정을 위해 블렌드 모드 설정
	SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

	// 윈도우 핸들 가져오기
	SDL_PropertiesID props = SDL_GetWindowProperties(window);
	HWND hwnd = (HWND)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
	// 윈도우 투명 및 클릭 무시 설정
	SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT);
	// 항상 위에 표시, 사이즈, 위치 변경, 활성화 금지 
	SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);

	for (int i = 0; i < number_particles; i++) {
		spawn_particle(&particles[i]);
	}

	// 비트맵 로드
	SDL_Surface* bmpSurface = SDL_LoadBMP("snow.bmp");
	if (!bmpSurface) {
		SDL_Log("Couldn't load BMP: %s", SDL_GetError());
		return SDL_APP_FAILURE;
	}

	// 컬러키(투명) 설정
	SDL_SetSurfaceColorKey(bmpSurface, true, SDL_MapSurfaceRGB(bmpSurface, 0x00, 0x00, 0x00));
	//SDL_SetSurfaceColorKey(bmpSurface, true, SDL_MapRGB(SDL_GetPixelFormatDetails(bmpSurface->format), NULL, 0x00, 0x00, 0x00));
	rect = { 0, 0, (float)bmpSurface->w, (float)bmpSurface->h };
	texture = SDL_CreateTextureFromSurface(renderer, bmpSurface);

	SDL_DestroySurface(bmpSurface);

	return SDL_APP_CONTINUE;
}

SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
{
	switch (event->type) {
	case SDL_EVENT_QUIT:
		return SDL_APP_SUCCESS;
	case SDL_EVENT_KEY_DOWN:
		printf("Key pressed: %s\n", SDL_GetKeyName(event->key.key));
		if (event->key.key == SDLK_ESCAPE)
			return SDL_APP_SUCCESS;
		break;
	default:
		break;
	}

	return SDL_APP_CONTINUE;
}

SDL_AppResult SDL_AppIterate(void* appstate)
{
	// FPS 계산. 사용하지는 않음.
	start = SDL_GetPerformanceCounter();

	{
		// 반투명 배경으로 클리어
		SDL_SetRenderDrawColor(renderer, 0, 0, 0, 128);
		SDL_RenderClear(renderer);

		draw_particle(particles);

		SDL_RenderPresent(renderer);
	}

	end = SDL_GetPerformanceCounter();
	fps = 1.0f / ((end - start) / (float)SDL_GetPerformanceFrequency());

	return SDL_APP_CONTINUE;
}

void SDL_AppQuit(void* appstate, SDL_AppResult result)
{
	SDL_DestroyTexture(texture);

	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	SDL_Quit();
}

 

※ 참고

위 코드에서는 화면 배율이 1이 아닌 경우를 위해 screen_width, screen_height 등의 정보를 Windows API를 이용해 확인하고, 화면 배율(display_scale)을 적용해 정확한 스크린 사이즈를 결정한다. 화면 배율이 1이 아닌 경우 정확한 스크린 사이즈 확인은 아래 링크와 같이 하는게 더 낫다.

 

2025.06.02 - [SDL, raylib] - [SDL3] Display Information 디스플레이 정보 가져오기

 

살짝 어두운 배경으로 바뀌고 눈이 내린다. 동시에 다른 프로그램을 사용할 수 있다.

 

 

※ 참고

2025.04.18 - [SDL, raylib] - [SDL3] Particle 파티클 (Fireworks/Snow)

 

반응형
Posted by J-sean
:
반응형

raylib에서 윈도우 핸들을 구해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#define _CRT_SECURE_NO_WARNINGS
 
#include "raylib.h"
#include <stdio.h>
 
int main(void)
{
    InitWindow(640480"raylib example");
 
    void* p = GetWindowHandle();
    printf("Window Handle: %p\n", p);
    //printf("%016llX\n", (long long)p);
 
    char str[17];
    sprintf(str, "%016llX", (long long)p);
 
    while (!WindowShouldClose())
    {
        BeginDrawing();
 
        ClearBackground(RAYWHITE);
        DrawText(str, 22023020, GRAY);
 
        EndDrawing();
    }
 
    CloseWindow();
 
    return 0;
}
 

 

소스를 입력하고 빌드한다.

 

마지막 줄에 윈도우 핸들이 출력된다.

 

윈도우 핸들이 출력된다.

하지만 raylib는 child window를 만들 수 없다.

 

※ 참고

2025.03.30 - [SDL, raylib] - [SDL3] Simple DirectMedia Layer 3 Setup and Getting Started - SDL3 설정 및 초기화

 

반응형
Posted by J-sean
:
반응형

투명한 윈도우를 만들어 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include "raylib.h"
 
int main(void)
{
    const int screenWidth = 450;
    const int screenHeight = 500;
 
    SetConfigFlags(FLAG_WINDOW_TRANSPARENT | FLAG_WINDOW_UNDECORATED);
    InitWindow(screenWidth, screenHeight, "raylib example");
    SetTargetFPS(60);
 
    Image rayimage = LoadImage("palvin.jpg");
    // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
    Texture2D texture = LoadTextureFromImage(rayimage);
 
    //Texture2D texture = LoadTexture("palvin.jpg");
    // Image 사용 없이 LoadTexture("palvin.jpg")로 해도 된다.
    // 이 경우, 아래 UnloadImage(rayimage);는 삭제한다.
 
    while (!WindowShouldClose())
    {
        BeginDrawing();
 
        ClearBackground(BLANK);
 
        DrawTexture(texture, 1010, WHITE);
 
        EndDrawing();
    }
 
    UnloadImage(rayimage);
    UnloadTexture(texture);
 
    CloseWindow();
 
    return 0;
}
 

 

소스를 입력하고 빌드한다.

 

타이틀바가 없고 배경이 투병한 윈도우에 이미지가 출력된다.

 

Window Flags

 

반응형

'SDL, raylib' 카테고리의 다른 글

[raylib] Window Handle 윈도우 핸들  (0) 2025.05.04
[raylib] Particle 파티클 (Snow)  (0) 2025.05.04
[raylib] raylib with opencv  (0) 2025.05.03
[raylib] raylib 설정하고 사용하기  (0) 2025.05.02
[SDL3] Framerate Per Second FPS  (0) 2025.04.22
Posted by J-sean
:
반응형

콘솔 윈도우(cmd)를 열지 않은 상태에서 명령을 실행하고 결과를 읽고 출력해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
    char psBuffer[128];
    FILE* pPipe;
 
    // Run command so that it writes its output to a pipe. Open this
    // pipe with read text attribute so that we can read it
    // like a text file.
    if ((pPipe = _popen("wmic baseboard get manufacturer, product, serialnumber""rt")) == NULL)
    {
        exit(1);
    }
 
    /* Read pipe until end of file, or an error occurs. */
    while (fgets(psBuffer, 128, pPipe))
    {
        puts(psBuffer);
    }
 
    int endOfFileVal = feof(pPipe);
    int closeReturnVal = _pclose(pPipe);
 
    if (endOfFileVal)
    {
        printf("\nProcess returned %d\n", closeReturnVal);
    } else
    {
        printf("Error: Failed to read the pipe to the end.\n");
    }
}
 

 

 

메인보드 정보가 출력된다.

 

아래와 같은 결과가 나온다면 wmic.exe를 프로젝트 폴더에 복사해 넣으면 된다.

'wmic'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.

Process returned 1

 

2026.03.28 - [C#] - [C#] 콘솔 프로그램 실행하고 결과 확인하기

 

반응형
Posted by J-sean
:

[SDL3] SDL3 Popup Window

SDL, raylib 2025. 4. 13. 13:06 |
반응형

SDL3에서 독립된 자식 윈도우가 아닌 팝업 윈도우를 생성해 보자.

부모 윈도우 영역의 일부처럼 동작한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <stdio.h>
#include <Windows.h>
 
static SDL_Window* window = NULL;
static SDL_Renderer* renderer = NULL;
 
static SDL_Window* popwindow = NULL;
static SDL_Renderer* poprenderer = NULL;
 
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[])
{
    SDL_SetAppMetadata("Example Renderer Clear""1.0""com.example.renderer-clear");
 
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    if (!SDL_CreateWindowAndRenderer("examples/renderer/clear"6404800&window, &renderer)) {
        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    popwindow = SDL_CreatePopupWindow(window, 00100100, SDL_WINDOW_TOOLTIP);
    if (!popwindow) {
        SDL_Log("Couldn't create popup window: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    poprenderer = SDL_CreateRenderer(popwindow, NULL);
    if (!poprenderer) {
        SDL_Log("Couldn't create popup renderer: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    SDL_PropertiesID prop = SDL_GetWindowProperties(window);
    HWND hwnd = (HWND)SDL_GetPointerProperty(prop, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
    if (hwnd)
        printf("Main window handle: %p\n", hwnd);
 
    prop = SDL_GetWindowProperties(popwindow);
    HWND hpwnd = (HWND)SDL_GetPointerProperty(prop, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
    if (hpwnd)
        printf("Popup window handle: %p\n", hpwnd);
 
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
{
    switch (event->type) {
    case SDL_EVENT_QUIT:
        return SDL_APP_SUCCESS;
    case SDL_EVENT_KEY_DOWN:
        printf("Key pressed: %s\n", SDL_GetKeyName(event->key.key));
        if (event->key.key == SDLK_ESCAPE)
            return SDL_APP_SUCCESS;
        break;
    default:
        break;
    }
 
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppIterate(void* appstate)
{
    SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);
 
    SDL_SetRenderDrawColor(poprenderer, 00255, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(poprenderer);
    SDL_RenderPresent(poprenderer);
 
    return SDL_APP_CONTINUE;
}
 
void SDL_AppQuit(void* appstate, SDL_AppResult result)
{
    SDL_DestroyRenderer(poprenderer);
    SDL_DestroyWindow(popwindow);
 
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
 
    SDL_Quit();
}
 

 

 

실행하면 팝업 윈도우 영역이 파란색으로 표시된다.

 

팝업 윈도우는 자신의 좌표를 갖는다. 위 코드를 수정한 아래 코드에서 확인해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
...
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[])
{
    ...
    popwindow = SDL_CreatePopupWindow(window, 5050100100, SDL_WINDOW_TOOLTIP);
    if (!popwindow) {
        SDL_Log("Couldn't create popup window: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    poprenderer = SDL_CreateRenderer(popwindow, NULL);
    if (!poprenderer) {
    SDL_Log("Couldn't create popup renderer: %s", SDL_GetError());
    return SDL_APP_FAILURE;
    ...
}
    ...
}
...
SDL_AppResult SDL_AppIterate(void* appstate)
{
    ...
    SDL_SetRenderDrawColor(poprenderer, 00255, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(poprenderer);
    SDL_SetRenderDrawColor(poprenderer, 25500, SDL_ALPHA_OPAQUE);
    SDL_RenderLine(poprenderer, 005050);
    SDL_RenderPresent(poprenderer);
 
    return SDL_APP_CONTINUE;
}
...
 

 

 

팝업 윈도우를 메인 윈도우 (50, 50) 위치에 (100, 100) 크기로 생성하고 (0, 0)부터 (50, 50)까지 빨간 직선을 그린다.

 

메인 윈도우가 아닌 팝업 윈도우의 좌상단 좌표 (0, 0)에서 (50, 50)까지 빨간 직선이 그려진다.

 

물론 (0, 0)에서 (300, 300) 까지 직선을 그려도 팝업 윈도우의 범위를 넘기 때문에 (100, 100)까지만 그려진다.

 

반응형
Posted by J-sean
:

Windows Mutex for Python

Python 2025. 2. 26. 11:28 |
반응형

파이썬에서 윈도우 뮤텍스를 사용해 보자.

 

아래 링크에서 namedmutex.py를 다운받고 파이썬 프로젝트에 포함시킨다.

namedmutex

namedmutex.py
0.00MB

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import namedmutex
#from time import sleep
 
# with namedmutex.NamedMutex('MyMutex'):
#     while True:
#         sleep(1)
#         print("Running")
 
mutex = namedmutex.NamedMutex('MyMutex')
if not mutex:
    print("Mutex not created.")
    exit(-1)
 
if not mutex.acquire(2):
    print("Mutex not acquired.")
    mutex.close()
    exit(-1)
 
input("Waiting...")
 
mutex.release()
mutex.close()
 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
using System.Threading;
 
public class Program
{
    public static void Main()
    {
        Mutex mutex = new Mutex(false"MyMutex");
        if (!mutex.WaitOne(2000))
        {
            Console.WriteLine("Signal not received.");
            mutex.Close();
            return;
        }
 
        Console.WriteLine("Waiting...");
        Console.ReadLine();
 
        mutex.ReleaseMutex();
        mutex.Close();
    }
}
 

 

위 파이썬 프로그램, C# 프로그램 중 중복을 포함하여 아무거나 두 개(두 번) 실행시켜 보면 MyMutex의 상황에 따라 실행 가능 여부를 확인 할 수 있다.

 

이번엔 뮤텍스를 사용해 프로그램의 중복 실행을 방지하는 코드를 살펴보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import namedmutex
 
mutex = namedmutex.NamedMutex('MyMutex')
if not mutex:
    print("Mutex not created.")
    exit(-1)
 
if not mutex.acquire(0):
    print("Program is running already.")
    mutex.close()
    exit(-1)
 
input("Waiting...")
 
mutex.release()
mutex.close()
 

 

위 프로그램을 실행한 상태에서 또  실행하면 두 번째 실행한 프로그램은 메세지가 출력되고 종료된다.

 

반응형
Posted by J-sean
:
반응형

윈도우 사이즈 변경 시 적용되는 뷰포트 화면 비율 모드를 설정해 보자.

 

스프라이트를 하나 추가한다.

 

실행하면 스프라이트가 나타난다.

 

윈도우 사이즈를 변경하면 화면이 잘린다.

 

Project Settings... - Display - Window - Stretch - Mode를 viewport로 변경한다.

 

 

사이즈를 변경하면 뷰포트의 비율이 유지된 상태로 늘어나거나 줄어든다.

 

Aspect를 expand로 변경한다.

 

뷰포트의 비율이 유지되지 않은 상태로 늘어나거나 줄어든다.

 

반응형
Posted by J-sean
: