Forgotten Coder craziest idea

Pages: 123
I modified the program I posted above to work with C++11. See below:

You may be able to compile the original by passing the flag -std=c++17 to your compiler.

The workflow is:
- Create a basic Win32 window.
- Allocate enough memory (see w32_screen_buffer::resize) to store color data for every pixel in the window. Call this a "screen buffer".
- Write arbitrary stuff (pretty pictures, a new frame of animation, whatever) into the screen buffer (see put_pixel).
- Periodically put the contents of the screen buffer onto the screen (see present_buffer).

This program is nearly as simple as it can be. There is only a little superficial functionality. Most notably we could make a mostly-cosmetic change by moving the code out of w32_screen_buffer and into the global namespace. There isn't a substantial benefit to doing so.

We could also simplify our program by removing functionality, for example, by removing the ability to resize the window or screen buffer.

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include <windows.h>

class w32_screen_buffer
{ 
  BITMAPINFO info_{};
  LPVOID     data_{};
    
public:
  ~w32_screen_buffer() { clear(); }
  w32_screen_buffer() = default;
  w32_screen_buffer(w32_screen_buffer const&)            = delete;
  w32_screen_buffer& operator=(w32_screen_buffer const&) = delete;
    
  LONG              width_px()  const { return info_.bmiHeader.biWidth;     }
  LONG              height_px() const { return -1*info_.bmiHeader.biHeight; }
  LPVOID            data()            { return data_; }
  LPCVOID           data()      const { return data_; }
  BITMAPINFO const& info()      const { return info_; }

  static LONG constexpr bytes_per_pixel = 4;
    
  bool resize(LONG new_width_px, LONG new_height_px)
  {
    SIZE_T const pixels_allocated = info_.bmiHeader.biWidth * info_.bmiHeader.biHeight;
    SIZE_T const pixels_requested = new_width_px * new_height_px;
    SIZE_T const bytes_requested  = bytes_per_pixel * pixels_requested;
  
    if (pixels_allocated < pixels_requested) clear(); else return true;

    // DOCS: https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfo
    info_.bmiHeader.biSize        = sizeof info_.bmiHeader;
    info_.bmiHeader.biWidth       = new_width_px;
    info_.bmiHeader.biBitCount    = 8 * bytes_per_pixel;
    info_.bmiHeader.biHeight      = -1 * new_height_px;
    info_.bmiHeader.biPlanes      = 1;
    info_.bmiHeader.biCompression = BI_RGB;
  
    data_ = VirtualAlloc(nullptr, bytes_requested, MEM_COMMIT, PAGE_READWRITE);
    if (! data_) clear();
      
    return data_;
  }

  void clear()
  {
    info_.bmiHeader.biWidth = 0;
    info_.bmiHeader.biHeight = 0;
      
    if (data_) VirtualFree(data_, 0, MEM_RELEASE);
  }
};

struct w32_window_dimension { LONG w, h; };
w32_window_dimension window_client_dimensions(HWND window)
{
  RECT r;
  GetClientRect(window, &r);
  return { r.right - r.left, r.bottom - r.top };
}
  
w32_screen_buffer screen_buffer;
bool running = true;

void present_buffer(w32_screen_buffer const& buf, HDC dc, LONG w_px, LONG h_px)
{
  StretchDIBits(dc, 0, 0, w_px, h_px, 0, 0, buf.width_px(), buf.height_px(),
                buf.data(), &buf.info(), DIB_RGB_COLORS, SRCCOPY);
}

void put_pixel(w32_screen_buffer& buf, LONG x, LONG y, ULONG color)
{
  LPBYTE p = reinterpret_cast<LPBYTE>(buf.data());
  LONG   w = buf.width_px();
    
  p += y * buf.bytes_per_pixel * w;
  p += x * buf.bytes_per_pixel;
    
  p[0] = static_cast<BYTE>((color & 0xff000000) >> 24);
  p[1] = static_cast<BYTE>((color & 0x00ff0000) >> 16);
  p[2] = static_cast<BYTE>((color & 0x0000ff00) >> 8);
  p[3] = static_cast<BYTE>((color & 0x000000ff) >> 0);
}

LRESULT CALLBACK my_wndproc(HWND window, UINT msg, WPARAM wp, LPARAM lp)
{
  switch (msg)
  {
  case WM_SIZE: 
    (void)::screen_buffer.resize(LOWORD(lp), HIWORD(lp));
    return 0;
  case WM_DESTROY:
  case WM_CLOSE: ::running = false;
    return 0;
  case WM_PAINT:
  {
    PAINTSTRUCT ps;
    HDC dc = BeginPaint(window, &ps);
    w32_window_dimension const dims = window_client_dimensions(window);
    present_buffer(::screen_buffer, dc, dims.w, dims.h);
    EndPaint(window, &ps);
    return 0;
  }
  default: return DefWindowProc(window, msg, wp, lp);
  }
}

int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR, int)
{
  LPCWSTR class_name = L"GDI Demo";
  WNDCLASSEXW wc {};
  wc.lpszClassName = class_name;
  wc.style         = CS_VREDRAW | CS_HREDRAW | CS_OWNDC;
  wc.lpfnWndProc   = my_wndproc;
  wc.cbSize        = sizeof wc;
  wc.hInstance     = instance;
  wc.hCursor       = LoadCursor(nullptr, IDC_ARROW);
  if (! RegisterClassExW(&wc)) return 1;

  HWND window =
    CreateWindowExW(0, wc.lpszClassName, wc.lpszClassName, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
                    instance, 0);
  if (! window) return 1;

  HDC dc = GetWindowDC(window);
  MSG message {};
  while (::running)
  {
    while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
      if (message.message == WM_QUIT) ::running = false; else DispatchMessage(&message);

    w32_window_dimension const dims = window_client_dimensions(window);

    // make the top left quarter of the screen red; basically
    // do all your video rendering here
    for (int x = 0; x < dims.w / 2; ++x) 
      for (int y = 0; y < dims.h / 2; ++y)
        put_pixel(::screen_buffer, x, y, 0xff00);
    
    present_buffer(::screen_buffer, dc, dims.w, dims.h);
  }

  return 0;
} 


Last edited on
you may want a function to do the color stuff (I thought there were built in macros for it using that color-ref integer rename?) around like 79. It comes up a lot.

what does shift by zero do?
p[3] = static_cast<BYTE>((color & 0x000000ff) >> 0);

that is pretty impressive fast work!
Last edited on
what does shift by zero do?

Nothing, it's just a little OCD sneaking in

Also, thanks :)
Hi mbozzi

Tested your last code in Codeblocks 20.03. No errors but i got a gray window not a red one. My screen resolution is 1920x1080.

Note: thanks for code.
Hi Furry guy

I change my editor and compiler to CodeBlocks 20.03. Now your code works and i got a black screen.

My last config of Visual Studio Code + compiler was giving an error of

undefined reference to `__imp_GetStockObject'

Note: thanks for coding tip
Linker errors like that mean that you didn't link the right libraries. Probably gdi32 (-lgdi32 with MinGW, link to gdi32.lib in Visual Studio).
Hi Ganado

I think you are right. I discover that Visual Studio Code has 3 files where you can configure things. Still strugling with that.

Note: thanks for tip
The libray will have simple commands like this:

x=0; y=0; width=100; height=200;
screen(x,y,width,height);
putpixel(10,10,RED);


Simplicity is inherently bad for UI libraries, because it brings various limitations to the library hard to deal with later.

The best UI library is the one that is just a thin wrapper around WinAPI and does it trough OOP.
Something like MFC but modern and self made.

Why "libraries die"? people are trying to make cross platform UI libraries that are simple to use and consistent across platform, with a nice look...

Problem is this takes a lot and a lot of time and testing, so limiting yourself to single platform and just as a thin layer saves you a lot of time and the result is flexibility and library that is easy to maintain and update as needed.

If you're looking for perfect, cross platform stuff that is simple to use (or trying to make one) you will fail.
Last edited on
I'd like to see the code as it develops. Have you thought of creating a github account, or other project sharing source?
Last edited on
Now that this topic has more than a bit of code in it maybe it is time to move it out of the Lounge and throw it over to another forum. Maybe the Windows one?
I'd like to see the code as it develops. Have you thought of creating a github account, or other project sharing source?


I do and I'm sharing some of the code but my UI is currently private :(


Here is sample window creation and constructor code and how it feels to make them, it's not very simple library but It's far from dealing with raw WinAPI which is productive enough for my taste.

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
int APIENTRY wWinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	PTSTR CmdLine,
	int CmdShow)
{
        auto pWindow = RefPtr<TestMainWindow>::create();
	
        pWindow->show(CmdShow, true);

        return pWindow->RunMessageLoop();
}

// And a constructor for sample test window:

TestMainWindow::TestMainWindow() noexcept(false) :
	BaseWindow(L"TestMainWindow", WindowProc<Initializer>),
	mpStyles(RefPtr<PushButton>::create()),
	mpHide(RefPtr<PushButton>::create())
{
	SetCaption(L"TestMainWindow");

	constexpr int width = 600;
	constexpr int height = 400;

	if (!initialize(
		GetSystemMetrics(SM_CXFULLSCREEN) / 2 - width / 2,
		GetSystemMetrics(SM_CYFULLSCREEN) / 2 - height / 2,
		width, height, nullptr, nullptr))
	{
		HandleException(wsl::ErrorCode::FunctionFailed);
	}

	mpStyles->SetCaption(L"Styles");
	const auto dim = mpStyles->GetRect();

	mpStyles->initialize(width - dim.right - 50, height - dim.bottom - 50, 100, 50, RefFromThis(), static_cast<DWORD>(Buttons::Style))

	mpStyles->AutoSize(true, true);

	mpSubWindow = RefPtr<SubWindow2>::create(RefFromThis());
	mpSubWindow->SetBackgroundColor(D2D1::ColorF::Green);

	const auto dim2 = mpHide->GetRect();

	if(!mpHide->initialize(width - dim2.right - 150, height - dim2.bottom - 50, 100, 50, RefFromThis(), static_cast<DWORD>(Buttons::Hide)))
		ShowError(ErrorCode::FunctionFailed);

	mpHide->SetCaption(L"Hide");
	mpHide->AutoSize(true, true);

	attach(mpSubWindow);
	attach(mpStyles);
	attach(mpHide);

	const auto handler = std::bind(&TestMainWindow::OnButtonClicked, this, std::placeholders::_1);
	mpStyles->mClickedEvent.add(handler);
	mpHide->mClickedEvent.add(handler);
}


Which creates this:
https://i.imgur.com/9rvMrj9.png
Last edited on
Hi malibor

It will be a library just for windows OS. And my testing will be in Windows 10. Cross-Platform is not in my plans for now. Why ? Because i don't know other platforms programming Big Lol.

I'm still searching for library name. Then i will decide to put it in a site or a git hub repository.

Note: thanks for code example. Too complicated for me. I started learning c++ only two weeks ago. I am not kiding. I am a newbie.
Hi newbieg

Has i told malibor I'm still searching for library name. Then i will decide to put library it in a site or a git hub repository.
Hi Furry Guy

I think i don't have the power to move my topic from lounge to windows forum. Maybe admistrator can do that. I don't know. I don't mind it moves to there. My idea of putting it in lounge was because i am talking about a personal project and stuff related to it. Maybe windows forum is more for another type of questions and i don't want to break any rules of that forum. That's my idea. I could be wrong of coarse. In lounge i can talk about anything so i come to the lounge.
You can move the topic. Edit the original post, and then you can select the forum.

The Lounge is for any talk other than programming.
Discussions about this website, or other topics not related to C++ programming

I'm liking this idea more and more since it's a project that can expand as you learn new skills.
Some ideas for next steps:
fillRectangle and drawLine.
Blitting whole areas of pixels is a lot more efficient than doing it per pixel.
Basic shapes would be nice. Rounded corner rectangles for button creation.
Mouse, keyboard focus, and other events.
If you do eventually get into the graphics card, then some things can be handled there like image scaling and rotation.

This guy has some videos you might find helpful: https://www.youtube.com/c/QuantitativeBytes
Last edited on
Hi Furry Guy

Tried moving this topic to "windows programming" forum and got message "This topic is too active to be moved.". Maybe it was this message Big Lol. I will try later.
Last edited on
I started learning c++ only two weeks ago. I am not kiding. I am a newbie.


If you just started learning then working on library will be very difficult for you, you should work on simple projects that are within or not far from the limits of your existing skills.

UI or graphics are out of question, but sample projects such as math library, various console programs or programs that are based on the book that you read should help to better memorize what you're learning.

just learning without writing anything concrete will soon make you forget things.
You can find a list of beginner program suggestions somewhere online and then try to implement them and learn new stuff as needed.

I suggest lists such as this one:
https://hackr.io/blog/cpp-projects
Last edited on
Hi malibor

I like a good chalenge. My current chalenge is to put the pixel on screen using a memory buffer with W32 stretchDIBits function. Got that idea from mbozzi code. Tried W32 Setpixel function but is too slow.

My memory buffer starts puting pixels from bottom left corner, line by line, all the way up on screen. It's bottom top. I want to start on top left corner moving down. Strugling with a working formula for that.
Last edited on
Strugling with a working formula for that.


I don't want to discourage you from doing advanced stuff but if you ignore to learn the language first then your struggle has just begun, the usual end of such struggle is loss of interest in programming. just saying...
Pages: 123