summaryrefslogtreecommitdiff
path: root/chromium/ui/gfx/x/event.cc
blob: 3d0637ca39b38958585abe57420949363093089c (plain)
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
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gfx/x/event.h"

#include <xcb/xcb.h>

#include <cstring>

#include "base/check_op.h"
#include "base/memory/scoped_refptr.h"
#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/xproto.h"
#include "ui/gfx/x/xproto_internal.h"
#include "ui/gfx/x/xproto_types.h"

namespace x11 {

Event::Event() = default;

Event::Event(scoped_refptr<base::RefCountedMemory> event_bytes,
             x11::Connection* connection,
             bool sequence_valid) {
  auto* xcb_event = reinterpret_cast<xcb_generic_event_t*>(
      const_cast<uint8_t*>(event_bytes->data()));
  sequence_valid_ = sequence_valid;
  sequence_ = xcb_event->full_sequence;
  // KeymapNotify events are the only events that don't have a sequence.
  if ((xcb_event->response_type & ~kSendEventMask) !=
      x11::KeymapNotifyEvent::opcode) {
    // On the wire, events are 32 bytes except for generic events which are
    // trailed by additional data.  XCB inserts an extended 4-byte sequence
    // between the 32-byte event and the additional data, so we need to shift
    // the additional data over by 4 bytes so the event is back in its wire
    // format, which is what Xlib and XProto are expecting.
    if ((xcb_event->response_type & ~kSendEventMask) ==
        x11::GeGenericEvent::opcode) {
      auto* ge = reinterpret_cast<xcb_ge_event_t*>(xcb_event);
      constexpr size_t ge_length = sizeof(xcb_raw_generic_event_t);
      constexpr size_t offset = sizeof(ge->full_sequence);
      size_t extended_length = ge->length * 4;
      if (extended_length < ge_length) {
        // If the additional data is smaller than the fixed size event, shift
        // the additional data to the left.
        memmove(&ge->full_sequence, &ge[1], extended_length);
      } else {
        // Otherwise shift the fixed size event to the right.
        char* addr = reinterpret_cast<char*>(xcb_event);
        memmove(addr + offset, addr, ge_length);
        event_bytes = base::MakeRefCounted<OffsetRefCountedMemory>(
            event_bytes, offset, ge_length + extended_length);
        xcb_event = reinterpret_cast<xcb_generic_event_t*>(addr + offset);
      }
    }
  }

  // Xlib sometimes modifies |xcb_event|, so let it handle the event after
  // we parse it with ReadEvent().
  ReadBuffer buf(event_bytes);
  ReadEvent(this, connection, &buf);
}

Event::Event(Event&& event) {
  memcpy(this, &event, sizeof(Event));
  memset(&event, 0, sizeof(Event));
}

Event& Event::operator=(Event&& event) {
  Dealloc();
  memcpy(this, &event, sizeof(Event));
  memset(&event, 0, sizeof(Event));
  return *this;
}

Event::~Event() {
  Dealloc();
}

void Event::Dealloc() {
  if (deleter_)
    deleter_(event_);
}

}  // namespace x11