How to move a no frame pygame windows when user clicks on it?

Write a function, which moves the window from dependent on a previous mouse position (start_x, start_y) and a mouse position (new_x, new_y)

def move_window(start_x, start_y, new_x, new_y):
        global window_size_x, window_size_y

        window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
        dist_x, dist_y = new_x - start_x, new_y - start_y
        environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)
 
        # Windows HACK
        window_size_x += 1 if window_size_x % 2 == 0 else -1 

        screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME)

In this function is a very important line:

window_size_x += 1 if window_size_x % 2 == 0 else -1

this line changes the width of the window from alternately by +1 and -1. On Windows systems there seems to be a bug, which ignores the new position parameter, if the size of the window didn’t change.
This “hack” is a workaround, which slightly change the size of the window whenever the position is changed.

A different approach, with no flickering, may look as follows. Note, though, that this version is significantly slower:

def move_window(start_x, start_y, new_x, new_y):
        global window_size_x, window_size_y
        buffer_screen = pygame.Surface((window_size_x, window_size_y))
        buffer_screen.blit(pygame.display.get_surface(), pygame.display.get_surface().get_rect())

        window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
        dist_x, dist_y = new_x - start_x, new_y - start_y
        environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)

        window_size_x += 1 if window_size_x % 2 == 0 else -1 

        screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME)
        screen.blit(buffer_screen, buffer_screen.get_rect())
        pygame.display.flip()

Change the position on MOUSEMOTION and MOUSEBUTTONUP:

def main():
    run = True
    pressed = False
    start_pos = (0,0)
    while run :

        # [...]

        for event in pygame.event.get():

            if event.type == MOUSEBUTTONDOWN:
                pressed = True
                start_pos = pygame.mouse.get_pos()

            elif event.type == MOUSEMOTION:
                if pressed:
                    new_pos = pygame.mouse.get_pos()
                    move_window(*start_pos, *new_pos)
                    pygame.event.clear(pygame.MOUSEBUTTONUP)

            elif event.type == MOUSEBUTTONUP:
                pressed = False
                new_pos = pygame.mouse.get_pos()
                move_window(*start_pos, *new_pos)

Full example program:

# coding : utf-8
import pygame
from pygame.locals import *
from os import environ
pygame.init()

clock = pygame.time.Clock()
window_size_x, window_size_y = 720, 360

infos = pygame.display.Info()
environ['SDL_VIDEO_WINDOW_POS'] = str(int(infos.current_w/2)) + ',' + str(int(infos.current_h/2)) 
screen = pygame.display.set_mode((window_size_x, window_size_x), pygame.NOFRAME)

def move_window(start_x, start_y, new_x, new_y): 
        global window_size_x, window_size_y

        window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
        dist_x, dist_y = new_x - start_x, new_y - start_y
        environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)

        window_size_x += 1 if window_size_x % 2 == 0 else -1
        screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME) 

def main():
    run = True
    pressed = False
    start_pos = (0,0)
    while run :
        screen.fill((255, 255, 255))
        pygame.display.update()
        clock.tick(60)

        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    run = False

            if event.type == MOUSEBUTTONDOWN:
                pressed = True
                start_pos = pygame.mouse.get_pos()

            elif event.type == MOUSEMOTION:
                if pressed:
                    new_pos = pygame.mouse.get_pos()
                    move_window(*start_pos, *new_pos)
                    pygame.event.clear(pygame.MOUSEBUTTONUP)

            elif event.type == MOUSEBUTTONUP:
                pressed = False
                new_pos = pygame.mouse.get_pos()
                move_window(*start_pos, *new_pos)

if __name__ == '__main__':
    main()

This solution no longer works under Windows systems and with Pygame 2.0. The position of a window can, however, be changed with the WINAPI function MoveWindow:

import pygame
from ctypes import windll

pygame.init()
screen = pygame.display.set_mode((400, 400), pygame.NOFRAME)
clock = pygame.time.Clock()

def moveWin(new_x, new_y):
    hwnd = pygame.display.get_wm_info()['window']
    w, h = pygame.display.get_surface().get_size()
    windll.user32.MoveWindow(hwnd, new_x, new_y, w, h, False)

window_pos = [100, 100]
moveWin(*window_pos)

run = True
while run :
    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                run = False
        elif event.type == pygame.MOUSEMOTION:
            if  pygame.mouse.get_pressed()[0]:
                window_pos[0] += event.rel[0]
                window_pos[1] += event.rel[1]
                moveWin(*window_pos)
    
    screen.fill((255, 255, 255))
    pygame.display.update()
    clock.tick(60)

Leave a Comment