• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Return-To-The-Roots / s25client / 22797872430

07 Mar 2026 11:05AM UTC coverage: 50.156% (-0.2%) from 50.327%
22797872430

Pull #1890

github

web-flow
Merge 4597ccbf9 into d2a3730c9
Pull Request #1890: Add support for borderless Windows

155 of 626 new or added lines in 44 files covered. (24.76%)

26 existing lines in 9 files now uncovered.

23041 of 45939 relevant lines covered (50.16%)

44513.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.77
/libs/s25main/Window.cpp
1
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "Window.h"
6
#include "CollisionDetection.h"
7
#include "Loader.h"
8
#include "RescaleWindowProp.h"
9
#include "commonDefines.h"
10
#include "controls/controls.h"
11
#include "driver/MouseCoords.h"
12
#include "drivers/ScreenResizeEvent.h"
13
#include "drivers/VideoDriverWrapper.h"
14
#include "helpers/containerUtils.h"
15
#include "ogl/IRenderer.h"
16
#include <boost/range/adaptor/map.hpp>
17
#include <boost/range/adaptor/reversed.hpp>
18
#include <cstdarg>
19

20
Window::Window(Window* parent, unsigned id, const DrawPoint& pos, const Extent& size)
1,249✔
21
    : parent_(parent), id_(id), pos_(pos), size_(size), active_(false), visible_(true), scale_(false),
22
      isInMouseRelay(false), animations_(this)
1,249✔
23
{}
1,249✔
24

25
Window::~Window()
1,249✔
26
{
27
    RTTR_Assert(!isInMouseRelay);
1,249✔
28
}
1,249✔
29

30
void Window::Draw()
356✔
31
{
32
    if(visible_)
356✔
33
        Draw_();
349✔
34
}
356✔
35

36
DrawPoint Window::GetPos() const
1,123✔
37
{
38
    return pos_;
1,123✔
39
}
40

41
DrawPoint Window::GetDrawPos() const
1,049✔
42
{
43
    DrawPoint result = pos_;
1,049✔
44
    const Window* temp = this;
1,049✔
45

46
    // Convert relative to absolute coordinates, i.e. sum positions of parents
47
    while(temp->parent_)
2,286✔
48
    {
49
        temp = temp->parent_;
1,237✔
50
        result += temp->pos_;
1,237✔
51
    }
52

53
    return result;
1,049✔
54
}
55

56
Extent Window::GetSize() const
5,933✔
57
{
58
    return size_;
5,933✔
59
}
60

61
Rect Window::GetDrawRect() const
660✔
62
{
63
    return Rect(GetDrawPos(), GetSize());
660✔
64
}
65

66
Rect Window::GetBoundaryRect() const
482✔
67
{
68
    // Default to draw rect
69
    return GetDrawRect();
482✔
70
}
71

72
bool Window::RelayKeyboardMessage(KeyboardMsgHandler msg, const KeyEvent& ke)
2✔
73
{
74
    // Ask derived classes whether relaying messages is allowed
75
    // (For example, ingame windows might not want to receive keyboard messages when they are minimized)
76
    if(!IsMessageRelayAllowed())
2✔
77
        return false;
×
78

79
    for(auto& wnd : childIdToWnd_ | boost::adaptors::map_values)
14✔
80
    {
81
        if(wnd->visible_ && wnd->active_ && CALL_MEMBER_FN(*wnd, msg)(ke))
12✔
82
            return true;
×
83
    }
84

85
    return false;
2✔
86
}
87

88
bool Window::RelayMouseMessage(MouseMsgHandler msg, const MouseCoords& mc)
113✔
89
{
90
    // Ask derived classes whether relaying messages is allowed
91
    if(!IsMessageRelayAllowed())
113✔
92
        return false;
×
93

94
    bool processed = false;
113✔
95
    isInMouseRelay = true;
113✔
96

97
    // Use reverse iterator because the topmost (=last elements) should receive the messages first!
98
    for(auto& wnd : childIdToWnd_ | boost::adaptors::map_values | boost::adaptors::reversed)
448✔
99
    {
100
        if(!lockedAreas_.empty() && IsInLockedRegion(mc.pos, wnd.get()))
335✔
101
            continue;
×
102

103
        if(wnd->visible_ && wnd->active_ && CALL_MEMBER_FN(*wnd, msg)(mc))
335✔
104
            processed = true;
1✔
105
    }
106

107
    for(auto* tofreeArea : tofreeAreas_)
113✔
108
        lockedAreas_.erase(tofreeArea);
×
109
    tofreeAreas_.clear();
113✔
110
    isInMouseRelay = false;
113✔
111

112
    return processed;
113✔
113
}
114

115
/**
116
 *  aktiviert das Fenster.
117
 *
118
 *  @param[in] activate Fenster aktivieren?
119
 */
120
void Window::SetActive(bool activate)
2,378✔
121
{
122
    active_ = activate;
2,378✔
123
    ActivateControls(activate);
2,378✔
124
}
2,378✔
125

126
/**
127
 *  aktiviert die Steuerelemente des Fensters.
128
 *
129
 *  @param[in] activate Steuerelemente aktivieren?
130
 */
131
void Window::ActivateControls(bool activate)
2,378✔
132
{
133
    for(auto& it : childIdToWnd_)
3,444✔
134
        it.second->SetActive(activate);
1,066✔
135
}
2,378✔
136

137
/**
138
 *  sperrt eine Region eines Fensters.
139
 *
140
 *  @param[in] window das Fenster, welches die Region sperrt.
141
 *  @param[in] rect   das Rechteck, welches die Region beschreibt.
142
 */
143
void Window::LockRegion(Window* window, const Rect& rect)
×
144
{
145
    lockedAreas_[window] = rect;
×
146
    auto it = helpers::find(tofreeAreas_, window);
×
147
    if(it != tofreeAreas_.end())
×
148
        tofreeAreas_.erase(it);
×
149

150
    // Also lock the region for all parents
151
    if(GetParent())
×
152
        GetParent()->LockRegion(this, rect);
×
153
}
×
154

155
/**
156
 *  Gibt eine gesperrte Region wieder frei.
157
 *
158
 *  @param[in] window das Fenster, welches die Region sperrt.
159
 */
160
void Window::FreeRegion(Window* window)
×
161
{
162
    // We need to keep all locked areas otherwise a closed dropdown will enable "click-through" to below control
163
    if(isInMouseRelay)
×
164
        tofreeAreas_.push_back(window);
×
165
    else
166
        lockedAreas_.erase(window);
×
167

168
    // Also free the locked region for all parents
169
    if(GetParent())
×
170
        GetParent()->FreeRegion(this);
×
171
}
×
172

173
void Window::SetPos(const DrawPoint& newPos)
1,350✔
174
{
175
    pos_ = newPos;
1,350✔
176
}
1,350✔
177

178
bool Window::IsMessageRelayAllowed() const
110✔
179
{
180
    return true;
110✔
181
}
182

183
void Window::DeleteCtrl(unsigned id)
2✔
184
{
185
    childIdToWnd_.erase(id);
2✔
186
}
2✔
187

188
ctrlBuildingIcon* Window::AddBuildingIcon(unsigned id, const DrawPoint& pos, BuildingType type, const Nation nation,
26✔
189
                                          unsigned short size, const std::string& tooltip)
190
{
191
    return AddCtrl(
26✔
192
      std::make_unique<ctrlBuildingIcon>(this, id, ScaleIf(pos), type, nation, ScaleIf(Extent(size, 0)).x, tooltip));
52✔
193
}
194

195
ctrlButton* Window::AddTextButton(unsigned id, const DrawPoint& pos, const Extent& size, const TextureColor tc,
86✔
196
                                  const std::string& text, const glFont* font, const std::string& tooltip)
197
{
198
    return AddCtrl(std::make_unique<ctrlTextButton>(this, id, ScaleIf(pos), ScaleIf(size), tc, text, font, tooltip));
86✔
199
}
200

201
ctrlButton* Window::AddColorButton(unsigned id, const DrawPoint& pos, const Extent& size, const TextureColor tc,
1✔
202
                                   const unsigned fillColor, const std::string& tooltip)
203
{
204
    return AddCtrl(std::make_unique<ctrlColorButton>(this, id, ScaleIf(pos), ScaleIf(size), tc, fillColor, tooltip));
1✔
205
}
206

207
ctrlButton* Window::AddImageButton(unsigned id, const DrawPoint& pos, const Extent& size, const TextureColor tc,
351✔
208
                                   ITexture* const image, const std::string& tooltip)
209
{
210
    return AddCtrl(std::make_unique<ctrlImageButton>(this, id, ScaleIf(pos), ScaleIf(size), tc, image, tooltip));
351✔
211
}
212

213
ctrlButton* Window::AddImageButton(unsigned id, const DrawPoint& pos, const Extent& size, const TextureColor tc,
351✔
214
                                   glArchivItem_Bitmap* const image, const std::string& tooltip)
215
{
216
    return AddImageButton(id, pos, size, tc, static_cast<ITexture*>(image), tooltip);
351✔
217
}
218

219
ctrlChat* Window::AddChatCtrl(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc,
2✔
220
                              const glFont* font)
221
{
222
    return AddCtrl(std::make_unique<ctrlChat>(this, id, ScaleIf(pos), ScaleIf(size), tc, font));
2✔
223
}
224

225
ctrlCheck* Window::AddCheckBox(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc,
49✔
226
                               const std::string& text, const glFont* font, bool readonly)
227
{
228
    return AddCtrl(std::make_unique<ctrlCheck>(this, id, ScaleIf(pos), ScaleIf(size), tc, text, font, readonly));
49✔
229
}
230

231
ctrlComboBox* Window::AddComboBox(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc,
55✔
232
                                  const glFont* font, unsigned short max_list_height, bool readonly)
233
{
234
    return AddCtrl(
55✔
235
      std::make_unique<ctrlComboBox>(this, id, ScaleIf(pos), ScaleIf(size), tc, font, max_list_height, readonly));
110✔
236
}
237

238
ctrlDeepening* Window::AddTextDeepening(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc,
9✔
239
                                        const std::string& text, const glFont* font, unsigned color, FontStyle style)
240
{
241
    return AddCtrl(
9✔
242
      std::make_unique<ctrlTextDeepening>(this, id, ScaleIf(pos), ScaleIf(size), tc, text, font, color, style));
18✔
243
}
244

245
ctrlDeepening* Window::AddColorDeepening(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc,
×
246
                                         unsigned fillColor)
247
{
NEW
248
    return AddCtrl(std::make_unique<ctrlColorDeepening>(this, id, ScaleIf(pos), ScaleIf(size), tc, fillColor));
×
249
}
250

251
ctrlDeepening* Window::AddImageDeepening(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc,
×
252
                                         ITexture* image)
253
{
NEW
254
    return AddCtrl(std::make_unique<ctrlImageDeepening>(this, id, ScaleIf(pos), ScaleIf(size), tc, image));
×
255
}
256

257
ctrlDeepening* Window::AddImageDeepening(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc,
×
258
                                         glArchivItem_Bitmap* image)
259
{
260
    return AddImageDeepening(id, pos, size, tc, static_cast<ITexture*>(image));
×
261
}
262

263
ctrlEdit* Window::AddEdit(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc, const glFont* font,
6✔
264
                          unsigned short maxlength, bool password, bool disabled, bool notify)
265
{
266
    return AddCtrl(std::make_unique<ctrlEdit>(this, id, ScaleIf(pos), ScaleIf(size), tc, font, maxlength, password,
12✔
267
                                              disabled, notify));
12✔
268
}
269

270
ctrlGroup* Window::AddGroup(unsigned id)
101✔
271
{
272
    return AddCtrl(std::make_unique<ctrlGroup>(this, id));
101✔
273
}
274

275
ctrlImage* Window::AddImage(unsigned id, const DrawPoint& pos, ITexture* image, const std::string& tooltip)
54✔
276
{
277
    return AddCtrl(std::make_unique<ctrlImage>(this, id, ScaleIf(pos), image, tooltip));
54✔
278
}
279

280
ctrlImage* Window::AddImage(unsigned id, const DrawPoint& pos, glArchivItem_Bitmap* image, const std::string& tooltip)
54✔
281
{
282
    return AddImage(id, pos, static_cast<ITexture*>(image), tooltip);
54✔
283
}
284

285
ctrlList* Window::AddList(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc, const glFont* font)
57✔
286
{
287
    return AddCtrl(std::make_unique<ctrlList>(this, id, ScaleIf(pos), ScaleIf(size), tc, font));
57✔
288
}
289

290
ctrlMultiline* Window::AddMultiline(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc,
13✔
291
                                    const glFont* font, FontStyle format)
292
{
293
    return AddCtrl(std::make_unique<ctrlMultiline>(this, id, ScaleIf(pos), ScaleIf(size), tc, font, format));
13✔
294
}
295

296
ctrlOptionGroup* Window::AddOptionGroup(unsigned id, GroupSelectType select_type)
4✔
297
{
298
    return AddCtrl(std::make_unique<ctrlOptionGroup>(this, id, select_type));
4✔
299
}
300

UNCOV
301
ctrlMultiSelectGroup* Window::AddMultiSelectGroup(unsigned id, GroupSelectType select_type)
×
302
{
NEW
303
    return AddCtrl(std::make_unique<ctrlMultiSelectGroup>(this, id, select_type));
×
304
}
305

306
ctrlPercent* Window::AddPercent(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc,
3✔
307
                                unsigned text_color, const glFont* font, const unsigned short* percentage)
308
{
309
    return AddCtrl(
3✔
310
      std::make_unique<ctrlPercent>(this, id, ScaleIf(pos), ScaleIf(size), tc, text_color, font, percentage));
6✔
311
}
312

313
ctrlProgress* Window::AddProgress(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc,
7✔
314
                                  unsigned short button_minus, unsigned short button_plus, unsigned short maximum,
315
                                  const std::string& tooltip, const Extent& padding, unsigned force_color,
316
                                  const std::string& button_minus_tooltip, const std::string& button_plus_tooltip)
317
{
318
    return AddCtrl(std::make_unique<ctrlProgress>(this, id, ScaleIf(pos), ScaleIf(size), tc, button_minus, button_plus,
14✔
319
                                                  maximum, padding, force_color, tooltip, button_minus_tooltip,
320
                                                  button_plus_tooltip));
14✔
321
}
322

323
ctrlScrollBar* Window::AddScrollBar(unsigned id, const DrawPoint& pos, const Extent& size, unsigned short button_height,
76✔
324
                                    TextureColor tc, unsigned short page_size)
325
{
326
    button_height = ScaleIf(Extent(0, button_height)).y;
76✔
327

328
    return AddCtrl(
76✔
329
      std::make_unique<ctrlScrollBar>(this, id, ScaleIf(pos), ScaleIf(size), button_height, tc, page_size));
152✔
330
}
331

332
ctrlTab* Window::AddTabCtrl(unsigned id, const DrawPoint& pos, unsigned short width)
3✔
333
{
334
    return AddCtrl(std::make_unique<ctrlTab>(this, id, ScaleIf(pos), ScaleIf(Extent(width, 0)).x));
3✔
335
}
336

UNCOV
337
ctrlTable* Window::AddTable(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc, const glFont* font,
×
338
                            std::vector<TableColumn> columns)
339
{
NEW
340
    return AddCtrl(std::make_unique<ctrlTable>(this, id, ScaleIf(pos), ScaleIf(size), tc, font, std::move(columns)));
×
341
}
342

343
ctrlTimer* Window::AddTimer(unsigned id, std::chrono::milliseconds timeout)
5✔
344
{
345
    return AddCtrl(std::make_unique<ctrlTimer>(this, id, timeout));
5✔
346
}
347

348
ctrlText* Window::AddText(unsigned id, const DrawPoint& pos, const std::string& text, unsigned color, FontStyle format,
166✔
349
                          const glFont* font)
350
{
351
    return AddCtrl(std::make_unique<ctrlText>(this, id, ScaleIf(pos), text, color, format, font));
166✔
352
}
353

354
ctrlMapSelection* Window::AddMapSelection(unsigned id, const DrawPoint& pos, const Extent& size,
×
355
                                          const SelectionMapInputData& inputData)
356
{
NEW
357
    return AddCtrl(std::make_unique<ctrlMapSelection>(this, id, ScaleIf(pos), ScaleIf(size), inputData));
×
358
}
359

360
TextFormatSetter Window::AddFormattedText(unsigned id, const DrawPoint& pos, const std::string& text, unsigned color,
×
361
                                          FontStyle format, const glFont* font)
362
{
363
    return AddText(id, pos, text, color, format, font);
×
364
}
365

366
ctrlVarDeepening* Window::AddVarDeepening(unsigned id, const DrawPoint& pos, const Extent& size, TextureColor tc,
1✔
367
                                          const std::string& formatstr, const glFont* font, unsigned color,
368
                                          unsigned parameters, ...)
369
{
370
    va_list liste;
371
    va_start(liste, parameters);
1✔
372

373
    auto ctrl = std::make_unique<ctrlVarDeepening>(this, id, ScaleIf(pos), ScaleIf(size), tc, formatstr, font, color,
1✔
374
                                                   parameters, liste);
1✔
375

376
    va_end(liste);
1✔
377

378
    return AddCtrl(std::move(ctrl));
2✔
379
}
380

UNCOV
381
ctrlVarText* Window::AddVarText(unsigned id, const DrawPoint& pos, const std::string& formatstr, unsigned color,
×
382
                                FontStyle format, const glFont* font, unsigned parameters, ...)
383
{
384
    va_list liste;
385
    va_start(liste, parameters);
×
386

387
    auto ctrl =
NEW
388
      std::make_unique<ctrlVarText>(this, id, ScaleIf(pos), formatstr, color, format, font, parameters, liste);
×
389

390
    va_end(liste);
×
391

NEW
392
    return AddCtrl(std::move(ctrl));
×
393
}
394

395
ctrlPreviewMinimap* Window::AddPreviewMinimap(const unsigned id, const DrawPoint& pos, const Extent& size,
×
396
                                              libsiedler2::ArchivItem_Map* const map)
397
{
NEW
398
    return AddCtrl(std::make_unique<ctrlPreviewMinimap>(this, id, ScaleIf(pos), ScaleIf(size), map));
×
399
}
400

401
void Window::Draw3D(const Rect& rect, TextureColor tc, bool elevated, bool highlighted, bool illuminated,
36✔
402
                    unsigned contentColor)
403
{
404
    const Extent rectSize = rect.getSize();
36✔
405
    if(rectSize.x < 4 || rectSize.y < 4)
36✔
406
        return;
×
407
    Draw3DBorder(rect, tc, elevated);
36✔
408
    // Move content inside border
409
    Rect contentRect(rect.getOrigin() + Position(2, 2), rectSize - Extent(4, 4));
36✔
410
    Draw3DContent(contentRect, tc, elevated, highlighted, illuminated, contentColor);
36✔
411
}
412

413
void Window::Draw3DBorder(const Rect& rect, TextureColor tc, bool elevated)
36✔
414
{
415
    if(tc == TextureColor::Invisible)
36✔
416
        return;
×
417
    glArchivItem_Bitmap* borderImg = LOADER.GetImageN("io", 12 + rttr::enum_cast(tc));
72✔
418
    VIDEODRIVER.GetRenderer()->Draw3DBorder(rect, elevated, *borderImg);
36✔
419
}
420

421
void Window::Draw3DContent(const Rect& rect, TextureColor tc, bool elevated, bool highlighted, bool illuminated,
68✔
422
                           unsigned contentColor)
423
{
424
    if(tc == TextureColor::Invisible)
68✔
425
        return;
×
426
    glArchivItem_Bitmap* contentImg = LOADER.GetImageN("io", rttr::enum_cast(tc) * 2 + (highlighted ? 0 : 1));
136✔
427
    VIDEODRIVER.GetRenderer()->Draw3DContent(rect, elevated, *contentImg, illuminated, contentColor);
68✔
428
}
429

430
void Window::DrawRectangle(const Rect& rect, unsigned color)
266✔
431
{
432
    VIDEODRIVER.GetRenderer()->DrawRect(rect, color);
266✔
433
}
266✔
434

435
void Window::DrawLine(DrawPoint pt1, DrawPoint pt2, unsigned short width, unsigned color)
×
436
{
437
    VIDEODRIVER.GetRenderer()->DrawLine(pt1, pt2, width, color);
×
438
}
×
439

440
void Window::Msg_PaintBefore()
305✔
441
{
442
    animations_.update(VIDEODRIVER.GetTickCount());
305✔
443
    for(auto& control : childIdToWnd_ | boost::adaptors::map_values)
479✔
444
        control->Msg_PaintBefore();
174✔
445
}
305✔
446

447
void Window::Msg_PaintAfter()
351✔
448
{
449
    for(auto& control : childIdToWnd_ | boost::adaptors::map_values)
544✔
450
        control->Msg_PaintAfter();
193✔
451
}
351✔
452

453
void Window::Draw_()
157✔
454
{
455
    for(auto& control : childIdToWnd_ | boost::adaptors::map_values)
340✔
456
        control->Draw();
183✔
457
}
157✔
458

459
void Window::Msg_ScreenResize(const ScreenResizeEvent& sr)
18✔
460
{
461
    // If the window elements don't get scaled there is nothing to do
462
    if(!scale_)
18✔
463
        return;
×
464
    RescaleWindowProp rescale(sr.oldSize, sr.newSize);
18✔
465
    for(auto& ctrl : childIdToWnd_ | boost::adaptors::map_values)
28✔
466
    {
467
        // Save new size (could otherwise be changed(?) in Msg_ScreenResize)
468
        Extent newSize = rescale(ctrl->GetSize());
10✔
469
        ctrl->SetPos(rescale(ctrl->GetPos()));
10✔
470
        ctrl->Msg_ScreenResize(sr);
10✔
471
        ctrl->Resize(newSize);
10✔
472
    }
473
    animations_.onRescale(sr);
18✔
474
}
475

476
template<class T_Pt>
477
T_Pt Window::Scale(const T_Pt& pt)
184✔
478
{
479
    return ScaleWindowPropUp::scale(pt, VIDEODRIVER.GetRenderSize());
184✔
480
}
481

482
template<class T_Pt>
483
T_Pt Window::ScaleIf(const T_Pt& pt) const
2,663✔
484
{
485
    return scale_ ? Scale(pt) : pt;
2,663✔
486
}
487

488
// Explicit template instantiations for the used types to avoid linker errors
489
template DrawPoint Window::Scale(const DrawPoint&);
490
template Extent Window::Scale(const Extent&);
491
template DrawPoint Window::ScaleIf(const DrawPoint&) const;
492
template Extent Window::ScaleIf(const Extent&) const;
493

494
bool Window::IsInLockedRegion(const Position& pos, const Window* exception) const
×
495
{
496
    for(const auto& lockEntry : lockedAreas_)
×
497
    {
498
        // Ignore exception
499
        if(lockEntry.first == exception)
×
500
            continue;
×
501
        if(IsPointInRect(pos, lockEntry.second))
×
502
            return true;
×
503
    }
504
    return false;
×
505
}
506

507
bool Window::IsMouseOver() const
138✔
508
{
509
    return IsMouseOver(VIDEODRIVER.GetMousePos());
138✔
510
}
511

512
bool Window::IsMouseOver(const MouseCoords& mousePos) const
465✔
513
{
514
    return IsPointInRect(mousePos.pos, GetBoundaryRect());
465✔
515
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc