mirror of https://github.com/OpenTTD/OpenTTD
Codechange: make Window destruction not rely on undefined behavior.
parent
f96f113951
commit
f6d5c0136e
136
src/window.cpp
136
src/window.cpp
|
@ -53,10 +53,8 @@ static Point _drag_delta; ///< delta between mouse cursor and upper left corner
|
||||||
static Window *_mouseover_last_w = nullptr; ///< Window of the last OnMouseOver event.
|
static Window *_mouseover_last_w = nullptr; ///< Window of the last OnMouseOver event.
|
||||||
static Window *_last_scroll_window = nullptr; ///< Window of the last scroll event.
|
static Window *_last_scroll_window = nullptr; ///< Window of the last scroll event.
|
||||||
|
|
||||||
/** List of windows opened at the screen sorted from the front. */
|
/** List of windows opened at the screen sorted from the front to back. */
|
||||||
Window *_z_front_window = nullptr;
|
WindowList _z_windows;
|
||||||
/** List of windows opened at the screen sorted from the back. */
|
|
||||||
Window *_z_back_window = nullptr;
|
|
||||||
|
|
||||||
/** If false, highlight is white, otherwise the by the widget defined colour. */
|
/** If false, highlight is white, otherwise the by the widget defined colour. */
|
||||||
bool _window_highlight_colour = false;
|
bool _window_highlight_colour = false;
|
||||||
|
@ -1109,16 +1107,7 @@ Window::~Window()
|
||||||
free(this->nested_array); // Contents is released through deletion of #nested_root.
|
free(this->nested_array); // Contents is released through deletion of #nested_root.
|
||||||
delete this->nested_root;
|
delete this->nested_root;
|
||||||
|
|
||||||
/*
|
*this->z_position = nullptr;
|
||||||
* Make fairly sure that this is written, and not "optimized" away.
|
|
||||||
* The delete operator is overwritten to not delete it; the deletion
|
|
||||||
* happens at a later moment in time after the window has been
|
|
||||||
* removed from the list of windows to prevent issues with items
|
|
||||||
* being removed during the iteration as not one but more windows
|
|
||||||
* may be removed by a single call to ~Window by means of the
|
|
||||||
* DeleteChildWindows function.
|
|
||||||
*/
|
|
||||||
const_cast<volatile WindowClass &>(this->window_class) = WC_INVALID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1231,7 +1220,7 @@ void ChangeWindowOwner(Owner old_owner, Owner new_owner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BringWindowToFront(Window *w);
|
static void BringWindowToFront(Window *w, bool dirty = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a window and make it the relative top-window on the screen.
|
* Find a window and make it the relative top-window on the screen.
|
||||||
|
@ -1351,90 +1340,23 @@ static uint GetWindowZPriority(WindowClass wc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a window to the z-ordering, according to its z-priority.
|
|
||||||
* @param w Window to add
|
|
||||||
*/
|
|
||||||
static void AddWindowToZOrdering(Window *w)
|
|
||||||
{
|
|
||||||
assert(w->z_front == nullptr && w->z_back == nullptr);
|
|
||||||
|
|
||||||
if (_z_front_window == nullptr) {
|
|
||||||
/* It's the only window. */
|
|
||||||
_z_front_window = _z_back_window = w;
|
|
||||||
w->z_front = w->z_back = nullptr;
|
|
||||||
} else {
|
|
||||||
/* Search down the z-ordering for its location. */
|
|
||||||
Window *v = _z_front_window;
|
|
||||||
uint last_z_priority = UINT_MAX;
|
|
||||||
(void)last_z_priority; // Unused without asserts
|
|
||||||
while (v != nullptr && (v->window_class == WC_INVALID || GetWindowZPriority(v->window_class) > GetWindowZPriority(w->window_class))) {
|
|
||||||
if (v->window_class != WC_INVALID) {
|
|
||||||
/* Sanity check z-ordering, while we're at it. */
|
|
||||||
assert(last_z_priority >= GetWindowZPriority(v->window_class));
|
|
||||||
last_z_priority = GetWindowZPriority(v->window_class);
|
|
||||||
}
|
|
||||||
|
|
||||||
v = v->z_back;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v == nullptr) {
|
|
||||||
/* It's the new back window. */
|
|
||||||
w->z_front = _z_back_window;
|
|
||||||
w->z_back = nullptr;
|
|
||||||
_z_back_window->z_back = w;
|
|
||||||
_z_back_window = w;
|
|
||||||
} else if (v == _z_front_window) {
|
|
||||||
/* It's the new front window. */
|
|
||||||
w->z_front = nullptr;
|
|
||||||
w->z_back = _z_front_window;
|
|
||||||
_z_front_window->z_front = w;
|
|
||||||
_z_front_window = w;
|
|
||||||
} else {
|
|
||||||
/* It's somewhere else in the z-ordering. */
|
|
||||||
w->z_front = v->z_front;
|
|
||||||
w->z_back = v;
|
|
||||||
v->z_front->z_back = w;
|
|
||||||
v->z_front = w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a window from the z-ordering.
|
|
||||||
* @param w Window to remove
|
|
||||||
*/
|
|
||||||
static void RemoveWindowFromZOrdering(Window *w)
|
|
||||||
{
|
|
||||||
if (w->z_front == nullptr) {
|
|
||||||
assert(_z_front_window == w);
|
|
||||||
_z_front_window = w->z_back;
|
|
||||||
} else {
|
|
||||||
w->z_front->z_back = w->z_back;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (w->z_back == nullptr) {
|
|
||||||
assert(_z_back_window == w);
|
|
||||||
_z_back_window = w->z_front;
|
|
||||||
} else {
|
|
||||||
w->z_back->z_front = w->z_front;
|
|
||||||
}
|
|
||||||
|
|
||||||
w->z_front = w->z_back = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On clicking on a window, make it the frontmost window of all windows with an equal
|
* On clicking on a window, make it the frontmost window of all windows with an equal
|
||||||
* or lower z-priority. The window is marked dirty for a repaint
|
* or lower z-priority. The window is marked dirty for a repaint
|
||||||
* @param w window that is put into the relative foreground
|
* @param w window that is put into the relative foreground
|
||||||
|
* @param dirty whether to mark the window dirty
|
||||||
*/
|
*/
|
||||||
static void BringWindowToFront(Window *w)
|
static void BringWindowToFront(Window *w, bool dirty)
|
||||||
{
|
{
|
||||||
RemoveWindowFromZOrdering(w);
|
auto priority = GetWindowZPriority(w->window_class);
|
||||||
AddWindowToZOrdering(w);
|
WindowList::iterator dest = _z_windows.begin();
|
||||||
|
while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest;
|
||||||
|
|
||||||
w->SetDirty();
|
if (dest != w->z_position) {
|
||||||
|
_z_windows.splice(dest, _z_windows, w->z_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty) w->SetDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1476,7 +1398,7 @@ void Window::InitializeData(WindowNumber window_number)
|
||||||
if (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != nullptr) SetFocusedWindow(this);
|
if (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != nullptr) SetFocusedWindow(this);
|
||||||
|
|
||||||
/* Insert the window into the correct location in the z-ordering. */
|
/* Insert the window into the correct location in the z-ordering. */
|
||||||
AddWindowToZOrdering(this);
|
BringWindowToFront(this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1848,6 +1770,7 @@ void Window::InitNested(WindowNumber window_number)
|
||||||
*/
|
*/
|
||||||
Window::Window(WindowDesc *desc) : window_desc(desc), mouse_capture_widget(-1)
|
Window::Window(WindowDesc *desc) : window_desc(desc), mouse_capture_widget(-1)
|
||||||
{
|
{
|
||||||
|
this->z_position = _z_windows.insert(_z_windows.end(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1875,8 +1798,6 @@ void InitWindowSystem()
|
||||||
{
|
{
|
||||||
IConsoleClose();
|
IConsoleClose();
|
||||||
|
|
||||||
_z_back_window = nullptr;
|
|
||||||
_z_front_window = nullptr;
|
|
||||||
_focused_window = nullptr;
|
_focused_window = nullptr;
|
||||||
_mouseover_last_w = nullptr;
|
_mouseover_last_w = nullptr;
|
||||||
_last_scroll_window = nullptr;
|
_last_scroll_window = nullptr;
|
||||||
|
@ -1898,14 +1819,7 @@ void UnInitWindowSystem()
|
||||||
|
|
||||||
for (Window *w : Window::Iterate()) delete w;
|
for (Window *w : Window::Iterate()) delete w;
|
||||||
|
|
||||||
for (Window *w = _z_front_window; w != nullptr; /* nothing */) {
|
_z_windows.clear();
|
||||||
Window *to_del = w;
|
|
||||||
w = w->z_back;
|
|
||||||
free(to_del);
|
|
||||||
}
|
|
||||||
|
|
||||||
_z_front_window = nullptr;
|
|
||||||
_z_back_window = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3068,15 +2982,13 @@ void InputLoop()
|
||||||
|
|
||||||
CheckSoftLimit();
|
CheckSoftLimit();
|
||||||
|
|
||||||
/* Do the actual free of the deleted windows. */
|
/* Remove dead entries from the window list */
|
||||||
for (Window *v = _z_front_window; v != nullptr; /* nothing */) {
|
for (auto it = _z_windows.begin(); it != _z_windows.end(); ) {
|
||||||
Window *w = v;
|
if (*it == nullptr) {
|
||||||
v = v->z_back;
|
it = _z_windows.erase(it);
|
||||||
|
} else {
|
||||||
if (w->window_class != WC_INVALID) continue;
|
++it;
|
||||||
|
}
|
||||||
RemoveWindowFromZOrdering(w);
|
|
||||||
free(w);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_input_events_this_tick != 0) {
|
if (_input_events_this_tick != 0) {
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#ifndef WINDOW_GUI_H
|
#ifndef WINDOW_GUI_H
|
||||||
#define WINDOW_GUI_H
|
#define WINDOW_GUI_H
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include "vehicle_type.h"
|
#include "vehicle_type.h"
|
||||||
#include "viewport_type.h"
|
#include "viewport_type.h"
|
||||||
#include "company_type.h"
|
#include "company_type.h"
|
||||||
|
@ -143,8 +145,8 @@ void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, Fra
|
||||||
void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align);
|
void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align);
|
||||||
|
|
||||||
/* window.cpp */
|
/* window.cpp */
|
||||||
extern Window *_z_front_window;
|
using WindowList = std::list<Window *>;
|
||||||
extern Window *_z_back_window;
|
extern WindowList _z_windows;
|
||||||
extern Window *_focused_window;
|
extern Window *_focused_window;
|
||||||
|
|
||||||
|
|
||||||
|
@ -293,19 +295,7 @@ public:
|
||||||
* to destruct them all at the same time too, which is kinda hard.
|
* to destruct them all at the same time too, which is kinda hard.
|
||||||
* @param size the amount of space not to allocate
|
* @param size the amount of space not to allocate
|
||||||
*/
|
*/
|
||||||
inline void *operator new[](size_t size)
|
inline void *operator new[](size_t size) = delete;
|
||||||
{
|
|
||||||
NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper allocation function to disallow something.
|
|
||||||
* Don't free the window directly; it corrupts the linked list when iterating
|
|
||||||
* @param ptr the pointer not to free
|
|
||||||
*/
|
|
||||||
inline void operator delete(void *ptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowDesc *window_desc; ///< Window description
|
WindowDesc *window_desc; ///< Window description
|
||||||
WindowFlags flags; ///< Window flags
|
WindowFlags flags; ///< Window flags
|
||||||
|
@ -336,8 +326,7 @@ public:
|
||||||
int mouse_capture_widget; ///< Widgetindex of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
|
int mouse_capture_widget; ///< Widgetindex of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
|
||||||
|
|
||||||
Window *parent; ///< Parent window.
|
Window *parent; ///< Parent window.
|
||||||
Window *z_front; ///< The window in front of us in z-order.
|
WindowList::iterator z_position;
|
||||||
Window *z_back; ///< The window behind us in z-order.
|
|
||||||
|
|
||||||
template <class NWID>
|
template <class NWID>
|
||||||
inline const NWID *GetWidget(uint widnum) const;
|
inline const NWID *GetWidget(uint widnum) const;
|
||||||
|
@ -813,9 +802,9 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterator to iterate all valid Windows
|
* Iterator to iterate all valid Windows
|
||||||
* @tparam Tfront Wether we iterate from front
|
* @tparam TtoBack whether we iterate towards the back.
|
||||||
*/
|
*/
|
||||||
template <bool Tfront>
|
template <bool TtoBack>
|
||||||
struct WindowIterator {
|
struct WindowIterator {
|
||||||
typedef Window *value_type;
|
typedef Window *value_type;
|
||||||
typedef value_type *pointer;
|
typedef value_type *pointer;
|
||||||
|
@ -823,22 +812,35 @@ public:
|
||||||
typedef size_t difference_type;
|
typedef size_t difference_type;
|
||||||
typedef std::forward_iterator_tag iterator_category;
|
typedef std::forward_iterator_tag iterator_category;
|
||||||
|
|
||||||
explicit WindowIterator(const Window *start) : w(const_cast<Window *>(start))
|
explicit WindowIterator(WindowList::iterator start) : it(start)
|
||||||
{
|
{
|
||||||
this->Validate();
|
this->Validate();
|
||||||
}
|
}
|
||||||
|
explicit WindowIterator(const Window *w) : it(w->z_position) {}
|
||||||
|
|
||||||
bool operator==(const WindowIterator &other) const { return this->w == other.w; }
|
bool operator==(const WindowIterator &other) const { return this->it == other.it; }
|
||||||
bool operator!=(const WindowIterator &other) const { return !(*this == other); }
|
bool operator!=(const WindowIterator &other) const { return !(*this == other); }
|
||||||
Window * operator*() const { return this->w; }
|
Window * operator*() const { return *this->it; }
|
||||||
WindowIterator & operator++() { this->Next(); this->Validate(); return *this; }
|
WindowIterator & operator++() { this->Next(); this->Validate(); return *this; }
|
||||||
|
|
||||||
bool IsEnd() const { return this->w == nullptr; }
|
bool IsEnd() const { return this->it == _z_windows.end(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Window *w;
|
WindowList::iterator it;
|
||||||
void Validate() { while (this->w != nullptr && this->w->window_class == WC_INVALID) this->Next(); }
|
void Validate()
|
||||||
void Next() { if (this->w != nullptr) this->w = Tfront ? this->w->z_back : this->w->z_front; }
|
{
|
||||||
|
while (!this->IsEnd() && *this->it == nullptr) this->Next();
|
||||||
|
}
|
||||||
|
void Next()
|
||||||
|
{
|
||||||
|
if constexpr (!TtoBack) {
|
||||||
|
++this->it;
|
||||||
|
} else if (this->it == _z_windows.begin()) {
|
||||||
|
this->it = _z_windows.end();
|
||||||
|
} else {
|
||||||
|
--this->it;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
using IteratorToFront = WindowIterator<false>; //!< Iterate in Z order towards front.
|
using IteratorToFront = WindowIterator<false>; //!< Iterate in Z order towards front.
|
||||||
using IteratorToBack = WindowIterator<true>; //!< Iterate in Z order towards back.
|
using IteratorToBack = WindowIterator<true>; //!< Iterate in Z order towards back.
|
||||||
|
@ -850,8 +852,17 @@ public:
|
||||||
template <bool Tfront>
|
template <bool Tfront>
|
||||||
struct AllWindows {
|
struct AllWindows {
|
||||||
AllWindows() {}
|
AllWindows() {}
|
||||||
WindowIterator<Tfront> begin() { return WindowIterator<Tfront>(Tfront ? _z_front_window : _z_back_window); }
|
WindowIterator<Tfront> begin()
|
||||||
WindowIterator<Tfront> end() { return WindowIterator<Tfront>(nullptr); }
|
{
|
||||||
|
if constexpr (Tfront) {
|
||||||
|
auto back = _z_windows.end();
|
||||||
|
if (back != _z_windows.begin()) --back;
|
||||||
|
return WindowIterator<Tfront>(back);
|
||||||
|
} else {
|
||||||
|
return WindowIterator<Tfront>(_z_windows.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowIterator<Tfront> end() { return WindowIterator<Tfront>(_z_windows.end()); }
|
||||||
};
|
};
|
||||||
using Iterate = AllWindows<false>; //!< Iterate all windows in whatever order is easiest.
|
using Iterate = AllWindows<false>; //!< Iterate all windows in whatever order is easiest.
|
||||||
using IterateFromBack = AllWindows<false>; //!< Iterate all windows in Z order from back to front.
|
using IterateFromBack = AllWindows<false>; //!< Iterate all windows in Z order from back to front.
|
||||||
|
|
Loading…
Reference in New Issue