Introduction

Welcome back to System Events and Integration. We are now in Lesson 2 of the course, so we already have one message-based program behind us. As you may recall from the previous lesson, a WinAPI window does not keep asking for work in a manual loop; instead, Windows sends messages, and we respond to them.

In this lesson, we will make that idea more interactive. We will capture the position of a left mouse click, store the coordinates, and repaint the window so the click becomes visible. By the end, our window will draw a small marker where the user clicked and show the exact xx and yy values inside the client area.

From Mouse Click To Visible Result

Before we write code, it helps to see the flow clearly. When the user presses the left mouse button, Windows sends the message WM_LBUTTONDOWN. That message carries the mouse position inside lParam. We do not usually draw right there. Instead, we save the data, request a repaint, and let WM_PAINT handle the drawing.

One detail matters a lot here: the position inside lParam is packed. The xx and yy values are stored together, so we should extract them with GET_X_LPARAM and GET_Y_LPARAM from windowsx.h. Those macros handle the signed conversion correctly, which makes them safer than manually pulling apart the bits.

Preparing Headers And Saved Coordinates

We begin with the headers and a pair of stored coordinates. These values need to stay available after the click happens, so we place them outside the window procedure as static globals.

The windowsx.h header provides the mouse coordinate macros this lesson depends on. We initialize g_clickX and g_clickY to -1 to serve as a simple signal that no click has been recorded yet.

Building The Message Handler

Next, we set up the WindowProc function, which is the heart of the program. Just like our timer example, this function receives messages one by one and decides what to do with each of them.

This structure should feel familiar. WM_LBUTTONDOWN handles input, WM_PAINT handles drawing, and WM_DESTROY ends the application cleanly. Any message we do not process ourselves is passed to DefWindowProcW, which keeps the window behaving like a normal WinAPI window.

Capturing The Click Position

Now we can fill in the left mouse button case. This is the core of the lesson: we decode the coordinates from lParam, store them, and ask Windows to repaint the window.

GET_X_LPARAM(lParam) and GET_Y_LPARAM(lParam) read the packed mouse position safely. The result is measured in client coordinates, so (0, 0) is the top-left corner of the window's drawing area. After saving the values, InvalidateRect marks the window as needing repainting. The NULL argument means the whole client area, and TRUE asks Windows to erase the background before drawing again.

Repainting At The Right Time

Once the window is invalidated, Windows eventually sends WM_PAINT. This is the proper place to draw. We begin painting, check whether a click exists, then finish painting in a clean and standard way.

BeginPaint gives us an HDC, which is the drawing context for the window. The PAINTSTRUCT ps is used to coordinate the paint cycle. The condition g_clickX != -1 prevents us from drawing a marker before the user clicks for the first time. This is a good example of separating state changes from visual updates: the click message updates the data, then the paint message uses that data to render the screen.

Drawing The Marker And Coordinate Text

Inside that if block, we create drawing tools, render a small circle centered on the click, build a text string, and show it near the top of the window.

A few details are worth noticing:

  • Ellipse uses a bounding box, so subtracting and adding 10 places the center exactly at the click.
  • swprintf formats the stored numbers into a wide string, and TextOutW draws that text in the client area.
  • SelectObject returns a handle to the object being replaced. We store these (hOldBrush, hOldPen) and re-select them into the HDC when finished. This is a critical GDI rule: never delete an object while it is still selected into a device context.
  • DeleteObject cleans up the brush and pen after they have been deselected, preventing memory leaks and resource exhaustion.
Conclusion and Next Steps

We now have a full WinAPI pattern for capturing mouse input data. We prepared persistent coordinate storage, extracted the packed position from lParam with GET_X_LPARAM and GET_Y_LPARAM, requested a repaint with InvalidateRect, and drew both a circular marker and formatted coordinate text during WM_PAINT.

Just as importantly, we reinforced a central WinAPI idea from the previous lesson: input handling and drawing are connected through the message system. We do not force the window to redraw immediately in the click handler; we update state first, then let WM_PAINT do the visual work in the proper place. In the practice section coming up, we will turn this flow into hands-on work so that reading mouse messages feels natural and fully under our control.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal